-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nested Preload loading incorrect data into model #4975
Comments
Actually it's not even just the case with nested Preloads, it seems this problem exists for directly referenced data as well. e.g. the following would have the same issue
|
To get around this I've ended up doing something like:
Which is a bit of a hack - I learnt that the same issue is also present with |
I took a look on your PR and got a few minor mistakes from your modelling.
This way when you're trying to create a HasMany the table where we have many items is where we should store a foreign key. The problem with the way you're modeled is that there's no key to discover which of the toys is the pet's favorite. To do it we could also store a foreign key on the pet table to make a HasOne relation with toys. And so we would have something like:
I'm also new to gorm so I couldn't find a way to store the foreignKey on the same Model we define the HasOne, so I've modeled it like this: type User struct {
gorm.Model
Pets []*Pet
}
type Pet struct {
gorm.Model
Name string
FavToy *Toy `gorm:"foreignKey:FavoritedBy;references:ID"`
Toys []*Toy `gorm:"foreignKey:ID"`
UserID uint
}
type Toy struct {
gorm.Model
Name string
PetID uint
FavoritedBy uint
} This way you can make your query by executing this: result := DB.
Preload("Pets").
Preload("Pets.FavToy").
Preload("Pets.Toys").
First(userFromDB) Also there's an error with the way you're saving the user database because you've already saved on pet1 and pet2 the userID relation key, so when you're executing pet1 := &Pet{Name: "pet1"}
pet2 := &Pet{Name: "pet2"}
DB.Create(pet1)
DB.Create(pet2)
user.Pets = []*Pet{pet1}
DB.Save(user) |
Thanks for the thorough review @eduardoths!
I didn't spot that the Pets table was missing the foreign key field. I've updated the PR to fix that (I was missing a
Ah yes, you're right. I've removed that line as it's not relevant for the test. In regards to to the DB modelling, I made a comment on the PR, but for the semantics of what I'm showing in this particular test case I'm really wanting the |
You're wellcome! So this is the models I came up with type User struct {
gorm.Model
Pets []*Pet
}
type Pet struct {
gorm.Model
Name string
FavToy *Toy `gorm:"foreignKey:FavToyID;references:ID"`
FavToyID *uint
Toy []*Toy
UserID uint
User *User
}
type Toy struct {
gorm.Model
Name string
PetID uint
} I don't know why, but I gorm wouldn't just automatically create the right constraint if I didn't specify it's the tag. There's also something special: FavToyID must be a pointer, not a int, because in this way we're telling gorm this relation is nullable and if we don't do that we're gonna have problems when inserting. package custom
import "gorm.io/gorm"
type Toy struct {
gorm.Model
Name string
PetID uint
} And then when trying to migrate I had to migrate the custom.Toy before Pet and then migrate the wanted Toy: func RunMigrations() {
var err error
allModels := []interface{}{&User{}, &Toy{}, &Pet{}}
rand.Seed(time.Now().UnixNano())
DB.Migrator().DropTable("user_friends", "user_speaks")
if err = DB.Migrator().DropTable(allModels...); err != nil {
log.Printf("Failed to drop table, got error %v\n", err)
os.Exit(1)
}
// This create a toy table withouth any constraints
if err = DB.AutoMigrate(&custom.Toy{}); err != nil {
log.Printf("Failed to auto migrate, but got error %v\n", err)
os.Exit(1)
}
if err = DB.AutoMigrate(allModels...); err != nil {
log.Printf("Failed to auto migrate, but got error %v\n", err)
os.Exit(1)
}
for _, m := range allModels {
if !DB.Migrator().HasTable(m) {
log.Printf("Failed to create table for %#v\n", m)
os.Exit(1)
}
}
} And by doing this it was finally possible to use preload for English is not my main language so let me know if something is not clear |
GORM Playground Link
go-gorm/playground#423
Description
I would like to preload a nested field from a model.
In the playground my models are set up as follows:
When I do:
I would expect the FavToy data to be populated based on the fav_toy_id field stored on the pets table in the DB.
However, in some cases the FavToy model is populated by one of the Pet's toys, but not their Fav Toy.
The text was updated successfully, but these errors were encountered: