GORM 中的反向外键查找
Reverse foreign key lookup in GORM
我正在尝试实现来自 GORM (https://gorm.io/docs/has_many.html#Has-Many) 的 'Has Many' 关联示例,其中一个 User
可以有多个 CreditCard
:
package main
import (
"fmt"
"os"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/sirupsen/logrus"
)
type User struct {
gorm.Model
CreditCards []CreditCard
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
const dbName = "examplegorm.db"
func main() {
db, err := gorm.Open("sqlite3", dbName)
if err != nil {
logrus.Fatalf("open db: %v", err)
}
defer func() {
db.Close()
os.Remove(dbName)
}()
db.LogMode(true)
db.AutoMigrate(&User{})
db.AutoMigrate(&CreditCard{})
user := User{}
if err := db.Create(&user).Error; err != nil {
logrus.Errorf("create user: %v", err)
}
fmt.Println(user.CreditCards)
creditCard := CreditCard{Number: "42", UserID: user.ID}
if err := db.Create(&creditCard).Error; err != nil {
logrus.Errorf("create credit card: %v", err)
}
db.First(&user, user.ID)
fmt.Println(user.CreditCards)
}
我希望 CreditCards
字段会自动更新为具有 UserID
的 CreditCard
对象,但是如果我 运行 这个脚本,我会看到 user.CreditCards
是添加信用卡前后的空切片:
kurt@Kurts-MacBook-Pro-13 ~/D/Scratch> go run gorm_has_many.go
(/Users/kurt/Documents/Scratch/gorm_has_many.go:36)
[2020-01-06 11:11:56] [0.88ms] CREATE TABLE "users" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"deleted_at" datetime )
[0 rows affected or returned ]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:36)
[2020-01-06 11:11:56] [0.65ms] CREATE INDEX idx_users_deleted_at ON "users"(deleted_at)
[0 rows affected or returned ]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:37)
[2020-01-06 11:11:56] [0.57ms] CREATE TABLE "credit_cards" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"deleted_at" datetime,"number" varchar(255),"user_id" integer )
[0 rows affected or returned ]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:37)
[2020-01-06 11:11:56] [0.55ms] CREATE INDEX idx_credit_cards_deleted_at ON "credit_cards"(deleted_at)
[0 rows affected or returned ]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:40)
[2020-01-06 11:11:56] [0.21ms] INSERT INTO "users" ("created_at","updated_at","deleted_at") VALUES ('2020-01-06 11:11:56','2020-01-06 11:11:56',NULL)
[1 rows affected or returned ]
[]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:47)
[2020-01-06 11:11:56] [0.23ms] INSERT INTO "credit_cards" ("created_at","updated_at","deleted_at","number","user_id") VALUES ('2020-01-06 11:11:56','2020-01-06 11:11:56',NULL,'42',1)
[1 rows affected or returned ]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:51)
[2020-01-06 11:11:56] [0.15ms] SELECT * FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" = 1 AND (("users"."id" = 1)) ORDER BY "users"."id" ASC LIMIT 1
[1 rows affected or returned ]
[]
我应该如何填充 CreditCards
字段?这不应该自动发生吗?
相比之下,如果我有以下 Django 模型(在名为 djangoapp
的应用程序中):
from django.db import models
class User(models.Model):
name = models.CharField(max_length=255)
age = models.PositiveIntegerField()
class CreditCard(models.Model):
number = models.CharField(max_length=255)
user = models.ForeignKey('djangoapp.User', on_delete=models.CASCADE)
然后在为用户添加信用卡后,我能够得到 user.creditcard_set
(参见 https://docs.djangoproject.com/en/3.0/topics/db/examples/many_to_one/):
In [5]: user = User.objects.create(name="jinzhu", age=20)
(0.002) INSERT INTO "djangoapp_user" ("name", "age") VALUES ('jinzhu', 20); args=['jinzhu', 20]
In [6]: credit_card = CreditCard.objects.create(number="42", user=user)
(0.002) INSERT INTO "djangoapp_creditcard" ("number", "user_id") VALUES ('42', 3); args=['42', 3]
In [8]: user.creditcard_set.all()
Out[8]: (0.000) SELECT "djangoapp_creditcard"."id", "djangoapp_creditcard"."number", "djangoapp_creditcard"."user_id" FROM "djangoapp_creditcard" WHERE "djangoapp_creditcard"."user_id" = 3 LIMIT 21; args=(3,)
<QuerySet [<CreditCard: CreditCard object (1)>]>
这个例子中的 CreditCards
不应该等同于 Django 的 creditcard_set
吗?
我通过简单地执行 Django 的 creditcard_set
在后台执行的查询来解决这个问题:
var creditCards []CreditCard
if err := db.Where("user_id = ?", user.ID).Find(&creditCards).Error; err != nil {
logrus.Errorf("get credit cards for user: %v", err)
}
不过,我还是有点不明白 GORM 文档示例中 User.CreditCards
字段的用途。
查询时需要Preload
CreditCards
获取。例如。
var user User
db.Preload("CreditCards").First(&user)
我正在尝试实现来自 GORM (https://gorm.io/docs/has_many.html#Has-Many) 的 'Has Many' 关联示例,其中一个 User
可以有多个 CreditCard
:
package main
import (
"fmt"
"os"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/sirupsen/logrus"
)
type User struct {
gorm.Model
CreditCards []CreditCard
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
const dbName = "examplegorm.db"
func main() {
db, err := gorm.Open("sqlite3", dbName)
if err != nil {
logrus.Fatalf("open db: %v", err)
}
defer func() {
db.Close()
os.Remove(dbName)
}()
db.LogMode(true)
db.AutoMigrate(&User{})
db.AutoMigrate(&CreditCard{})
user := User{}
if err := db.Create(&user).Error; err != nil {
logrus.Errorf("create user: %v", err)
}
fmt.Println(user.CreditCards)
creditCard := CreditCard{Number: "42", UserID: user.ID}
if err := db.Create(&creditCard).Error; err != nil {
logrus.Errorf("create credit card: %v", err)
}
db.First(&user, user.ID)
fmt.Println(user.CreditCards)
}
我希望 CreditCards
字段会自动更新为具有 UserID
的 CreditCard
对象,但是如果我 运行 这个脚本,我会看到 user.CreditCards
是添加信用卡前后的空切片:
kurt@Kurts-MacBook-Pro-13 ~/D/Scratch> go run gorm_has_many.go
(/Users/kurt/Documents/Scratch/gorm_has_many.go:36)
[2020-01-06 11:11:56] [0.88ms] CREATE TABLE "users" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"deleted_at" datetime )
[0 rows affected or returned ]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:36)
[2020-01-06 11:11:56] [0.65ms] CREATE INDEX idx_users_deleted_at ON "users"(deleted_at)
[0 rows affected or returned ]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:37)
[2020-01-06 11:11:56] [0.57ms] CREATE TABLE "credit_cards" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"deleted_at" datetime,"number" varchar(255),"user_id" integer )
[0 rows affected or returned ]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:37)
[2020-01-06 11:11:56] [0.55ms] CREATE INDEX idx_credit_cards_deleted_at ON "credit_cards"(deleted_at)
[0 rows affected or returned ]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:40)
[2020-01-06 11:11:56] [0.21ms] INSERT INTO "users" ("created_at","updated_at","deleted_at") VALUES ('2020-01-06 11:11:56','2020-01-06 11:11:56',NULL)
[1 rows affected or returned ]
[]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:47)
[2020-01-06 11:11:56] [0.23ms] INSERT INTO "credit_cards" ("created_at","updated_at","deleted_at","number","user_id") VALUES ('2020-01-06 11:11:56','2020-01-06 11:11:56',NULL,'42',1)
[1 rows affected or returned ]
(/Users/kurt/Documents/Scratch/gorm_has_many.go:51)
[2020-01-06 11:11:56] [0.15ms] SELECT * FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" = 1 AND (("users"."id" = 1)) ORDER BY "users"."id" ASC LIMIT 1
[1 rows affected or returned ]
[]
我应该如何填充 CreditCards
字段?这不应该自动发生吗?
相比之下,如果我有以下 Django 模型(在名为 djangoapp
的应用程序中):
from django.db import models
class User(models.Model):
name = models.CharField(max_length=255)
age = models.PositiveIntegerField()
class CreditCard(models.Model):
number = models.CharField(max_length=255)
user = models.ForeignKey('djangoapp.User', on_delete=models.CASCADE)
然后在为用户添加信用卡后,我能够得到 user.creditcard_set
(参见 https://docs.djangoproject.com/en/3.0/topics/db/examples/many_to_one/):
In [5]: user = User.objects.create(name="jinzhu", age=20)
(0.002) INSERT INTO "djangoapp_user" ("name", "age") VALUES ('jinzhu', 20); args=['jinzhu', 20]
In [6]: credit_card = CreditCard.objects.create(number="42", user=user)
(0.002) INSERT INTO "djangoapp_creditcard" ("number", "user_id") VALUES ('42', 3); args=['42', 3]
In [8]: user.creditcard_set.all()
Out[8]: (0.000) SELECT "djangoapp_creditcard"."id", "djangoapp_creditcard"."number", "djangoapp_creditcard"."user_id" FROM "djangoapp_creditcard" WHERE "djangoapp_creditcard"."user_id" = 3 LIMIT 21; args=(3,)
<QuerySet [<CreditCard: CreditCard object (1)>]>
这个例子中的 CreditCards
不应该等同于 Django 的 creditcard_set
吗?
我通过简单地执行 Django 的 creditcard_set
在后台执行的查询来解决这个问题:
var creditCards []CreditCard
if err := db.Where("user_id = ?", user.ID).Find(&creditCards).Error; err != nil {
logrus.Errorf("get credit cards for user: %v", err)
}
不过,我还是有点不明白 GORM 文档示例中 User.CreditCards
字段的用途。
查询时需要Preload
CreditCards
获取。例如。
var user User
db.Preload("CreditCards").First(&user)