gorm使用之各种表关系实例-主外键->struct
一对多关系(用户与文章)
如:
老板与员工
女神和舔狗
老师和学生
班级与学生
用户与文章
...
以用户与文章举例
models应当如,注意!!:User表中的ID应当与Article中的UID一直,大小和注释都得一只
package models
type User struct {
ID uint
Name string `gorm:"size:8"`
Articles []Article `gorm:"foreignKey:UID"` // 用户拥有的文章列表
}
type Article struct {
ID uint `gorm:"size:4"`
Title string `gorm:"size:16"`
UID uint // 属于
User User `gorm:"foreignKey:UID"` // 属于
}
联表实例
增
改
查
删
一对一关系(用户与用户详情)
一对一关系比较少,一般用于表的扩展
例如一张用户表,有很多字段
那么就可以把它拆分为两张表,常用的字段放主表,不常用的字段放详情表
表结构搭建
type User struct {
ID uint
Name string
Age int
Gender bool
UserInfo UserInfo // 通过UserInfo可以拿到用户详情信息
}
type UserInfo struct {
UserID uint // 外键
ID uint
Addr string
Like string
}
增
添加用户详情,关联已有用户
这个场景特别适合网站的注册,以及后续信息完善
刚开始注册的时候,只需要填写很基本的信息,这就是添加主表的一条记录
注册进去之后,去个人中心,添加头像,修改地址…
这就是添加附表
删
改
DB.Create(&UserInfo{
UserID: 2,
Addr: "南京市",
Like: "吃饭",
})
查
一般是主表查附表
var user User
DB.Preload("UserInfo").Take(&user)
fmt.Println(user)
多对多关系(文章标签,文章标签表)
多对多关系,需要用第三张表存储两张表的关系
假设表结构如:
type Article struct {
ID uint
Title string
Tags []Tag `gorm:"many2many:article_tags"`
}
type Tag struct {
ID uint
Name string
}
type ArticleTag struct {
ArticleID uint `gorm:"primaryKey"`
TagID uint `gorm:"primaryKey"`
CreatedAt time.Time
}
生成表结构
// 设置Article的Tags表为ArticleTag
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
// 如果tag要反向应用Article,那么也得加上
// DB.SetupJoinTable(&Tag{}, "Articles", &ArticleTag{})
err := DB.AutoMigrate(&Article{}, &Tag{}, &ArticleTag{})
fmt.Println(err)
操作案例
举一些简单的例子
-
添加文章并添加标签,并自动关联
-
添加文章,关联已有标签
-
给已有文章关联标签
-
替换已有文章的标签
-
添加文章并添加标签,并自动关联
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{}) // 要设置这个,才能走到我们自定义的连接表
DB.Create(&Article{
Title: "flask零基础入门",
Tags: []Tag{
{Name: "python"},
{Name: "后端"},
{Name: "web"},
},
})
// CreatedAt time.Time 由于我们设置的是CreatedAt,gorm会自动填充当前时间,
// 如果是其他的字段,需要使用到ArticleTag 的添加钩子 BeforeCreate
- 添加文章,关联已有标签
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
var tags []Tag
DB.Find(&tags, "name in ?", []string{"python", "web"})
DB.Create(&Article{
Title: "flask请求对象",
Tags: tags,
})
- 给已有文章关联标签
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
article := Article{
Title: "django基础",
}
DB.Create(&article)
var at Article
var tags []Tag
DB.Find(&tags, "name in ?", []string{"python", "web"})
DB.Take(&at, article.ID).Association("Tags").Append(tags)
- 替换已有文章的标签
var article Article
var tags []Tag
DB.Find(&tags, "name in ?", []string{"后端"})
DB.Take(&article, "title = ?", "django基础")
DB.Model(&article).Association("Tags").Replace(tags)
- 查询文章列表,显示标签
var articles []Article
DB.Preload("Tags").Find(&articles)
fmt.Println(articles)
自定义连接表主键
这个功能还是很有用的,例如你的文章表 可能叫ArticleModel,你的标签表可能叫TagModel
那么按照gorm默认的主键名,那就分别是ArticleModelID,TagModelID,太长了,根本就不实用
这个地方,官网给的例子看着也比较迷,不过我已经跑通了
主要是要修改这两项
joinForeignKey 连接的主键id
JoinReferences 关联的主键id
type ArticleModel struct {
ID uint
Title string
Tags []TagModel `gorm:"many2many:article_tags;joinForeignKey:ArticleID;JoinReferences:TagID"`
}
type TagModel struct {
ID uint
Name string
Articles []ArticleModel `gorm:"many2many:article_tags;joinForeignKey:TagID;JoinReferences:ArticleID"`
}
type ArticleTagModel struct {
ArticleID uint `gorm:"primaryKey"` // article_id
TagID uint `gorm:"primaryKey"` // tag_id
CreatedAt time.Time
}
生成表结构
DB.SetupJoinTable(&ArticleModel{}, "Tags", &ArticleTagModel{})
DB.SetupJoinTable(&TagModel{}, "Articles", &ArticleTagModel{})
err := DB.AutoMigrate(&ArticleModel{}, &TagModel{}, &ArticleTagModel{})
fmt.Println(err)
添加,更新,查询操作和上面的都是一样
操作连接表
如果通过一张表去操作连接表,这样会比较麻烦
比如查询某篇文章关联了哪些标签
或者是举个更通用的例子,用户和文章,某个用户在什么时候收藏了哪篇文章
无论是通过用户关联文章,还是文章关联用户都不太好查
最简单的就是直接查连接表
type UserModel struct {
ID uint
Name string
Collects []ArticleModel `gorm:"many2many:user_collect_models;joinForeignKey:UserID;JoinReferences:ArticleID"`
}
type ArticleModel struct {
ID uint
Title string
// 这里也可以反向引用,根据文章查哪些用户收藏了
}
// UserCollectModel 用户收藏文章表
type UserCollectModel struct {
UserID uint `gorm:"primaryKey"` // article_id
ArticleID uint `gorm:"primaryKey"` // tag_id
CreatedAt time.Time
}
func main() {
DB.SetupJoinTable(&UserModel{}, "Collects", &UserCollectModel{})
err := DB.AutoMigrate(&UserModel{}, &ArticleModel{}, &UserCollectModel{})
fmt.Println(err)
}
常用的操作就是根据用户查收藏的文章列表
var user UserModel
DB.Preload("Collects").Take(&user, "name = ?", "枫枫")
fmt.Println(user)
但是这样不太好做分页,并且也拿不到收藏文章的时间
var collects []UserCollectModel
DB.Find(&collects, "user_id = ?", 2)
fmt.Println(collects)
这样虽然可以查到用户id,文章id,收藏的时间,但是搜索只能根据用户id搜,返回也拿不到用户名,文章标题等
我们需要改一下表结构,不需要重新迁移,加一些字段
type UserModel struct {
ID uint
Name string
Collects []ArticleModel `gorm:"many2many:user_collect_models;joinForeignKey:UserID;JoinReferences:ArticleID"`
}
type ArticleModel struct {
ID uint
Title string
}
// UserCollectModel 用户收藏文章表
type UserCollectModel struct {
UserID uint `gorm:"primaryKey"` // article_id
UserModel UserModel `gorm:"foreignKey:UserID"`
ArticleID uint `gorm:"primaryKey"` // tag_id
ArticleModel ArticleModel `gorm:"foreignKey:ArticleID"`
CreatedAt time.Time
}
查询
var collects []UserCollectModel
var user UserModel
DB.Take(&user, "name = ?", "枫枫")
// 这里用map的原因是如果没查到,那就会查0值,如果是struct,则会忽略零值,全部查询
DB.Debug().Preload("UserModel").Preload("ArticleModel").Where(map[string]any{"user_id": user.ID}).Find(&collects)
for _, collect := range collects {
fmt.Println(collect)
}