Go migration doesn't create a foreign key - go

My first day with Go. I try to develop a migration mechanism with GORM within Revel.
These are my model structs:
type Role struct {
gorm.Model
Name string
}
type User struct {
gorm.Model
Name string
Role Role `gorm:"foreignkey:RoleIdForRole;association_foreignkey:Id"`
}
I simply automigrate both structs as follows, which work fine. I see tables called users and roles but users table doesn't have a field role_id or roleID
db.AutoMigrate(&models.Role{})
db.AutoMigrate(&models.User{})
What am I missing?

I found a solution.
First of all, although Tim Brown's answer is a valid answer compatible with documentation, it doesn't work. As mentioned in many forum posts and github issues, GORM foreign key is not autogenerated as told in the documentation.
But this helps:
db.Model(&models.User{}).AddForeignKey("role_id", "roles(id)", "RESTRICT", "RESTRICT")
Shortly you need to set foreign keys just after migrating the tables.

You need a RoleID column, and if you name it that you wouldn't need to declare the foreignkey either.
type User struct {
gorm.Model
Name string
RoleID int `gorm:"column:RoleIdForRole"`
Role Role
}
See this page for more info

Related

Does GORM support attibutes of many to many relationships?

Using the gorm.io library, how would I implement a many to many relationship, where the relationship itself has attributes?
Example:
Without the attribute I would do it something like this, but I can't find a solution to my Problem in the GORM docs.
type Recipe struct {
gorm.Model
Name string
Ingredients []Ingredient `gorm:"many2many"`
}
type Ingredient struct {
gorm.Model
Name string
}

Should I explicitly create a relation symmetrical to "Belongs To" or "Has Many"?

I am new to ORM (and GORM) so apologies if this is an obvious question, but it does not seem to be covered by the documentation.
I will be using the examples from the documentation as a base to my questions
Question 1: Belongs To
// `User` belongs to `Company`, `CompanyID` is the foreign key
type User struct {
gorm.Model
Name string
CompanyID int
Company Company
}
type Company struct {
ID int
Name string
}
A User belongs to one Company only → this is handled by the code
above
A Company has many User → is this implied by the code
above? Or should I add somehow a relation O2M in Company?
Question 2: Has Many
// User has many CreditCards, UserID is the foreign key
type User struct {
gorm.Model
CreditCards []CreditCard
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
A User has 1+ CreditCard→ this is handled by the code
A CreditCard can belong to several users (say, a shared family CC) → is it implied? (if not: how to set up the O2M relationship).
Or is it, instead, a case where a CreditCard is explicitly configured to belong to only one user?
Q1: Based on how you defined your structs, you don't need an explicit O2M relationship in the Company struct, but when loading Company details, if you want to load all users that are assigned to that specific company, you need to add that field as well. It will need an additional function call like Preload or Joins, but you shouldn't need an explicit definition of this relationship.
type Company struct {
ID int
Name string
Users []User
}
Q2: The way the relationship is defined now, it is configured so that a CreditCard belongs to only one user. If you want a many2many relationship, you need to specify the relation table. There is more documentation on it here, but it should look something like this:
type User struct {
gorm.Model
CreditCards []CreditCard `gorm:"many2many:users_creditcards"`
}
type CreditCard struct {
gorm.Model
Number string
}

GORM AutoMigrate Has One & Has Many:

I want to create a model User and Social where the User model has many Socials. Ideally a Social type would also have has one relation to simpilify querying from either side. Here is a code sample:
database type is MySQL 8.0
type base struct {
ID string `json:"id" gorm:"type:char(36);primaryKey;"`
Created time.Time `json:"created" gorm:"autoCreateTime"`
Updated time.Time `json:"updated" gorm:"autoUpdateTime"`
}
type User struct {
base
Friends []*User `json:"friends" gorm:"many2many:friends"`
Socials []*Social `json:"socials"`
}
type Social struct {
base
Provider string `json:"provider" gorm:"type:varchar(32);index"`
Identifier string `json:"identifier" gorm:"type:varchar(32);index"`
User *User `json:"user" gorm:"foreignKey:ID"`
Token string `json:"token"`
Link string `json:"link" gorm:"type:varchar(128)"`
}
Im getting the following error when using db.AutoMigrate(&User{}, &Social{}):
model.Social's field User, need to define a valid foreign key for relations or it need to implement the Valuer/Scanner interface
runtime error: invalid memory address or nil pointer dereference
I have tried:
adding gorm:"foreignKey:ID" to User.Socials tags
not using pointers (eg in User struct Socials []Social instead of Socials []*Social)
but the issue remains
According to documentation (https://gorm.io/docs/has_many.html#Has-Many),
you need to use objects, not references
type User struct {
base
Friends []User `json:"friends" gorm:"many2many:friends"`
Socials []Social `json:"socials"`
}
no * here
Also you can add UserID field to Social
type Social struct {
base
UserID string
Provider string `json:"provider" gorm:"type:varchar(32);index"`
Identifier string `json:"identifier" gorm:"type:varchar(32);index"`
User *User `json:"user" gorm:"foreignKey:ID"`
Token string `json:"token"`
Link string `json:"link" gorm:"type:varchar(128)"`
}
and add
type User struct {
base
FriendOf string `gorm:""`
Friends []*User `json:"friends" gorm:"many2many:friends,foreignKey:FriendOf"`
Socials []*Social `json:"socials"`
}
Issue was here:
type base struct {
...
}
type User {
base
...
}
type Social {
base
...
}
Since I thought base would just be package local definition I messed up the capitalization and had a private primary key.
Another issue was touched on by #vodolaz095, but (imo) not sufficiently clarified for any new go-gorm user.
It does not seem to be possible to use a has one relation like User User be the foreign key for a has many relation like Socials []Social gorm:"foreignKey:User". It is required to be split up as #vodolaz095 displayed in his 2nd code block

What does Preload function do in gorm?

The link http://gorm.io/docs/preload.html talks about preloading in GORM, but I am unable to understand what this function does.
type User struct {
gorm.Model
Username string
Orders Order
}
type Order struct {
gorm.Model
UserID uint
Price float64
}
db.Preload("Username")
db.Preload("Orders").Find(&users)
Can someone explain what will these 2 statements do? What will be the output ?
Is it that preload is used to cache the results?
The link you provided shows what it does. You just have to actually read the information on the page.
First of all, this doesn't really do anything:
db.Preload("Username")
On the other hand, the following:
db.Preload("Orders").Find(&users)
Does do something. First it populates users and then it populates []user.Orders. On the comments on the page you linked you will find this, which shows what it does by query:
db.Preload("Orders").Find(&users)
//// SELECT * FROM users;
//// SELECT * FROM orders WHERE user_id IN (1,2,3,4);
So what does this really do? I can give you the technical answer, which you can easily find out by googling eager loading or I can give you an answer by example which I feel is simpler.
So let me explain this by example.
Say you have users, where each user can have multiple orders. This is a one to many relationship which can be defined like:
type User struct {
gorm.Model
Username string
Orders []Order
}
When you populate your users slice like:
db.Find(&users)
//// SELECT * FROM users;
If you need to get all the orders for each user you can easily access user.Orders but this would be empty anyway because it didn't get populated.
If we populate users slice like:
db.Preload("Orders").Find(&users)
The user.Orders will be populated by the orders of that user. This is an abstraction that makes dealing with relationships easier.
The ugly alternative would be:
db.Find(&users)
for user := range users {
db.Where("user_id", user.id).Find(&user.Orders)
}
It will also make more request to the database than necessary. (which is not good)
If your users has Posts and Comments and Orders then you can define and query it like this:
type User struct {
gorm.Model
Username string
Orders []Order
Comments []Comment
Posts []Post
}
db.Preload("Orders").Preload("Comments").Preload("Posts").Find(&users)
With just the code above you can now have access to the users data in different tables in the database.
I hope that helps.

Gorm - upgrading columns constraint with migration

How do you perform a Migration with gorm? For example, I need to add a constraint to a column. I changed my model (simplified example below), but the AutoMigrate method, according to the docs, will not change column's constraints.
How do you achieve it then? I cannot find anything useful in the docs
Starting model:
type User struct {
gorm.Model
Name string
}
I would like to update it like this:
type User struct {
gorm.Model
Name string `gorm:"not null"`
}
When adding a not null constraint, then a default value will also need to be added for when the existing records do not meet the new criteria. The definition could be something like:
type User struct {
gorm.Model
Name string `gorm:"not null;default:'fillertext'"`
}
As #putu said, ALTER TABLE will certainly work, but you would still need to add a default value initially to ensure the non-comforming rows meet the new requirements. Once this has been done you can remove the default value if you desire and the migration will work as intended from that point on.

Resources