完美解决 gorm表初始化 外键报错、define a valid foreign key for ...

在使用GORM的创建foreignKey关系的时,不管是按照官方文档给的例子写,还是说加上`gorm:"foreignKey:ID;references:UserId;"` 这样的,都是一样报错:define a valid foreign key for ... ,网上大部分给出的解决方案是gorm后面直接用"-"。如下图

 
type User struct {
	gorm.Model
	UserName    string    `json:"user_name" gorm:"comment:用户名"`
	Blogs       []Blog`    json:"blogs" gorm:"-"`
}
 
type Blog struct {
	gorm.Model
	Title       string    `json:"title      "gorm:"comment:标题"`
	Content     string    `json:"Content    "gorm:"comment:正文"`
    UserId      uint      `json:"user_id    "gorm:"comment:作者ID"`
}

这样确实可以绕过这个错误,但是官方提供的预加载(Preload)等高级方法用不了,

完美解决办法:

我们可以将需要创建外键的表拆开来写,还是上面的例子,我们可以这样写

type BaseUser struct {
	gorm.Model
	UserName    string    `json:"user_name" gorm:"comment:用户名"`
}
 
// 设置表名
func (BaseUser) TableName() string {
	return "user"
}
 
 
type User struct {
    BaseUser 
	Blogs     []Blog`   json:"blogs" gorm:"foreignKey:Id;references:UserId;"`
}
 
// 设置表名
func (User) TableName() string {
	return "user"
}
 
 
type Blog struct {
	gorm.Model
	Title       string    `json:"title      "gorm:"comment:标题"`
	Content     string    `json:"Content    "gorm:"comment:正文"`
    UserId      uint      `json:"user_id    "gorm:"comment:作者ID"`
}

这样我们在执行db.Debug().Migrator().AutoMigrate()创建表的时候创建 BaseUser,但是在代码中查询的时候使用User;如下图,可以查询出所有的用户,并且每个用户的所有blog也会一次性查询出来,避免了写原生SQL查询或者要多次循环才能查询出每个用户的所有blog

func  FindUser(db *gorm.DB) error {
	var (
		err error
        users = []User{}
	)
	err = db.Debug().
		Model(&User{}).
		Preload("Blogs").  //这里要注意
		Find(&users).
		Error
	if err != nil {
		log.Errorf("db error: %s", err)
		return err
	}
	return err
}

同样如果需要在查询Blog列表的时候想要直接查出作者的信息,就需要在Blog表中加上user,如下图:

type BaseUser struct {
	gorm.Model
	UserName    string    `json:"user_name" gorm:"comment:用户名"`
}
 
// 设置表名
func (BaseUser) TableName() string {
	return "user"
}
 
 
type User struct {
    BaseUser 
	Blogs     []BaseBlog  `json:"blogs" gorm:"foreignKey:Id;references:UserId"`
}
 
// 设置表名
func (User) TableName() string {
	return "user"
}
 
 
type BaseBlog struct {
	gorm.Model
	Title       string    `json:"title      "gorm:"comment:标题"`
	Content     string    `json:"Content    "gorm:"comment:正文"`
    UserId      uint      `json:"user_id    "gorm:"comment:作者ID"`
}
 
// 设置表名
func (BaseBlog) TableName() string {
	return "blog"
}
 
type Blog struct {
	BaseBlog 
    User User  `json:"user" gorm:"foreignKey:UserId;references:Id;"`
}
// 设置表名
func (Blog) TableName() string {
	return "blog"
}

查询blog列表的时候也是同样操作,就可以同时查询出User的字段

func  FindUser(db *gorm.DB) error {
	var (
		err error
        Blogs = []Blog{}
	)
	err = db.Debug().
		Model(&Blog{}).
		Preload("User").
		Find(&Blogs ).
		Error
	if err != nil {
		log.Errorf("db error: %s", err)
		return err
	}
	return err
}

  当然还有其他很多高级的用法,比如在查询用户列表时候除了可以同时查询出该用户的所有blog,还可以对blog进行统计,需要稍微改一下User表结构,并使用GORM钩子,如下

type BaseUser struct {
	gorm.Model
	UserName    string    `json:"user_name" gorm:"comment:用户名"`
}
 
// 设置表名
func (BaseUser) TableName() string {
	return "user"
}
 
 
type User struct {
    BaseUser 
	Blogs         []BaseBlog  `json:"blogs" gorm:"foreignKey:Id;references:UserId"`
    BlogsCount    int         `json:"blogs_count" gorm:"-"` //这样的只是为了返回给前端,没必要创建数据库字段,所以增加了gorm:"-"
}
 
// 设置表名
func (User) TableName() string {
	return "user"
}
 
 
// 利用GORM钩子对每个用户的blog进行统计
func (e *User) AfterFind(_ *gorm.DB) error {
	e.BlogsCount = len(e.Blogs)
	return nil
}
 
 
 
type BaseBlog struct {
	gorm.Model
	Title       string    `json:"title      "gorm:"comment:标题"`
	Content     string    `json:"Content    "gorm:"comment:正文"`
    UserId      uint      `json:"user_id    "gorm:"comment:作者ID"`
}
 
// 设置表名
func (BaseBlog) TableName() string {
	return "blog"
}
 
type Blog struct {
	BaseBlog 
    User User  `json:"user" gorm:"foreignKey:UserId;references:Id;"`
}
// 设置表名
func (Blog) TableName() string {
	return "blog"
}

这样可以完美解决建表的错误,还能使用Preload等高级方法,并且我们知道如果数据库真的创建外键之后有很多数据库约束,维护起来很麻烦,这样做数据库中还不会创建外键,非常完美的解决多个问题。更多GORM高级用法可以参考官方文档GORM Guides | GORM - The fantastic ORM library for Golang, aims to be developer friendly.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值