gorm使用之各种表关系实例-主外键->struct

news2025/3/10 18:22:49

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"` // 属于
}

联表实例

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Image2

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Image5

一对一关系(用户与用户详情)

一对一关系比较少,一般用于表的扩展

例如一张用户表,有很多字段

那么就可以把它拆分为两张表,常用的字段放主表,不常用的字段放详情表

表结构搭建

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:   "吃饭",
})

Image8

一般是主表查附表

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)

操作案例

举一些简单的例子

  1. 添加文章并添加标签,并自动关联

  2. 添加文章,关联已有标签

  3. 给已有文章关联标签

  4. 替换已有文章的标签

  5. 添加文章并添加标签,并自动关联

DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})  // 要设置这个,才能走到我们自定义的连接表
DB.Create(&Article{
  Title: "flask零基础入门",
  Tags: []Tag{
    {Name: "python"},
    {Name: "后端"}, 
    {Name: "web"},
  },
})
// CreatedAt time.Time 由于我们设置的是CreatedAt,gorm会自动填充当前时间,
// 如果是其他的字段,需要使用到ArticleTag 的添加钩子 BeforeCreate
  1. 添加文章,关联已有标签
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
var tags []Tag
DB.Find(&tags, "name in ?", []string{"python", "web"})
DB.Create(&Article{
  Title: "flask请求对象",
  Tags:  tags,
})
  1. 给已有文章关联标签
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)
  1. 替换已有文章的标签
var article Article
var tags []Tag
DB.Find(&tags, "name in ?", []string{"后端"})
DB.Take(&article, "title = ?", "django基础")
DB.Model(&article).Association("Tags").Replace(tags)
  1. 查询文章列表,显示标签
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)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1197943.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

搞怪python代码

微信消息重发代码: from pynput.keyboard import Key,Controller import time keyboard Controller()a input("请输入你需要循环输出的内容:") b eval(input(请输入你想要循环的次数:)) print("数据已接收!请将…

基于51单片机的篮球比赛计分器积分器

wx供重浩:创享日记 对话框发送:单片机篮球 获取完整源程序仿真源文件原理图文件论文报告等 基于51单片机的篮球计分器 由STC89C51单片机数码管显示模块按键模块电源模块构成 具体功能: (1)能记录单节比赛的比赛时间&am…

msvcp140.dll丢失的解决方法win7系统,全面详细解析

在Windows 7系统中,msvcp140.dll是一个非常重要的动态链接库文件,它负责许多应用程序和系统的正常运行。然而,由于各种原因,msvcp140.dll文件可能会丢失或损坏,导致系统出现错误提示、程序无法启动等问题。本文将详细介…

leetcode(力扣) 207. 课程表1+2(图的构造与遍历,清晰思路,完整模拟)

文章目录 题目描述思路分析完整代码 题目描述 你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] [ai, bi] ,表示如果要学…

赛氪ETTBL全国商务英语翻译大赛入榜国内翻译赛事发展评估报告

中国外文局下属CATTI项目管理中心出具2023 国内翻译赛事发展评估报告,ETTBL全国商务英语翻译大赛赫然在榜 2023年11月6日,继2022年首次发布国内翻译赛事发展评估报告后,中国外文局CATTI项目管理中心和中国外文界平台联合发布了《2023国内翻译…

OpenCV图像坐标系

绘制代码: X轴 # 选取两个点 point1 = (20, 0) point2 = (200, 0)# 在图像上绘制连接线 cv2.line(img, point1, point2, (

C# ZXing 二维码,条形码生成与识别

C# ZXing 二维码条形码生成识别 安装ZXing使用ZXing生成条形码生成二维码生成带Logo的二维码识别二维码、条形码 安装ZXing NuGet搜索ZXing安装ZXing.Net包 使用ZXing using ZXing; using ZXing.Common; using ZXing.QrCode; using ZXing.QrCode.Internal; 生成条形码 //…

MYSQL内容补充:

一)联合索引: 1)定义:是给一张表上面的多个列增加索引,也就是说给表上面的多个列增加索引,供快速查询使用,当两个列的组合是唯一值时,联合索引是个不错的选择 联合索引和单个索引对比来讲,联合索引的所有索引项都会出现…

maven 私有仓库配置

1.整体库信息 2.配置阿里云库 &#xff08;可以配置多个库&#xff0c;再引用代理库&#xff09; 3.建立自己的 发布&#xff0c;快照库 4.建立自由的公共库- 引用所有需要的库 5.maven setting 中配置 用户名密码 <server><id>mv-releases</id><usernam…

IDEA运行前端vue项目,安装nodejs,以及配置

我在刚接手到一个项目的时候&#xff0c;不知道前端的代码的情况下&#xff0c;想要写后端代码&#xff0c;遇到问题 所以需要看前台代码&#xff0c;着手IDEA 开始 安装nodejs (为什么要安装nodejs呢&#xff0c;首先就是说需要npm, 而nodejs 内置npm) 1.从官网下载 nodej…

字符三角形-第10届蓝桥杯国赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第6讲。 字符三角形&#x…

基于DS1302时钟液晶12864显示2路闹钟仿真及源程序

一、系统方案 1、本设计采用51单片机作为主控器。 2、DS1302采集年月日时分秒送到液晶12864显示。 3、按键年月日时分秒&#xff0c;两路闹钟。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 uchar clock_time[6] {0X00,0X59,0X23,0X09,0X…

【带头学C++】----- 四、动态内存空间申请 ---- 4.1 动态内存分配

1.动态内存分配概述 在C和C等语言中&#xff0c;可以使用malloc、calloc、realloc或使用new等函数来动态分配内存空间&#xff0c;同时使用free、delete函数释放动态分配的内存空间&#xff0c;这样可以根据程序的实际需要动态管理内存&#xff0c;避免静态内存分配的局限性。 …

Windows查看端口占用情况

Windows如何查看端口占用情况 方法1. cmd命令行执行netstat命令&#xff0c;查看端口占用情况 netstat -ano 以上命令输出太多信息&#xff0c;不方便查看&#xff0c;通过如下命令搜索具体端口占用情况&#xff0c;例如&#xff1a;8080端口 netstat -ano | findstr "…

STM32--时钟树

一、什么是时钟&#xff1f; 时钟是单片机的脉搏&#xff0c;是系统工作的同步节拍。单片机上至CPU&#xff0c;下至总线外设&#xff0c;它们工作时序的配合&#xff0c;都需要一个同步的时钟信号来统一指挥。时钟信号是周期性的脉冲信号。 二、什么是时钟树&#xff1f; S…

可视化技术专栏100例教程导航帖—学习可视化技术的指南宝典

&#x1f389;&#x1f38a;&#x1f389; 你的技术旅程将在这里启航&#xff01; &#x1f680;&#x1f680; 本文专栏&#xff1a;可视化技术专栏100例 可视化技术专栏100例领略各种先进的可视化技术&#xff0c;包括但不限于大屏可视化、图表可视化等等。订阅专栏用户在文章…

两两交换链表中的节点 --- 递归回溯算法练习四

目录 1. 分析题意 2. 分析算法原理 2.1. 递归思路&#xff1a; 1. 挖掘子问题 3. 编写代码 3.1. step 1&#xff1a; 3.2. step 2&#xff1a; 3.3. step 3&#xff1a; 3.4. 递归代码 1. 分析题意 力扣上原题链接如下&#xff1a; 24. 两两交换链表中的节点 - 力扣&am…

【Linux】Linux 中关于 MySQL 的相关操作

Linux 中关于 MySQL 的相关操作 Linux 系统与 MySQL 数据库是目前互联网开发中最为流行的组合之一。Linux 作为开源的操作系统&#xff0c;具有运行效率高、安全性好等优点&#xff1b;而 MySQL 作为开源的数据库&#xff0c;具有运行速度快、可靠性高等特点。 &#xff08;1&…

Panda3d 场景管理

Panda3d 场景管理 文章目录 Panda3d 场景管理有关分层场景图的重要信息NodePathNodePath 以及 Node 的函数调用模型文件文件格式加载模型文件将模型放置在场景图中模型缓存压缩模型异步加载模型通过回调函数进行 常见的状态变化修改节点的位置和姿态改变父级节点改变颜色隐藏和…

前端视角中的微信登录

目录 引入 流程介绍 具体实现 引入 本文主要讲解网站应用中微信登录的具体流程是怎么样的&#xff0c;以及作为前端开发人员在这整个流程中的主要任务是什么。 如果想要实现微信登录的功能&#xff0c;需要开发人员到微信开放平台注册相应的账号&#xff0c;进行注册应用&am…