• 完美解决 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.

  • 相关阅读:
    @weakify, @strongify ObjC的Block中使用weakSelf/strongSelf @weakify/@strongify
    __block 与 __weak的区别理解
    RunTime的一些用法
    关于block使用的5点注意事项
    为什么你要拒绝我 ——苹果AppStore被拒理由大全
    IOS 音频开发文件大小计算
    Servlet一次乱码排查后的总结(转)
    android.support.v7.app.AppCompatActivity
    android如何切换皮肤
    android include中的控件调用
  • 原文地址:https://www.cnblogs.com/name/p/16418800.html
Copyright © 2020-2023  润新知