Django ORM 框架中的表关系,你真的弄懂了吗?

2023-10-27

Django ORM 框架中的表关系

为了说清楚问题,我们设计一个 crm 系统,包含五张表:

1.tb_student 学生表

2.tb_student_detail 学生详情表

3.tb_salesman 课程顾问表

4.tb_course 课程表

5.tb_entry 报名表

表关系和字段如下图:

  如果你想学习自动化测试,我这边给你推荐一套视频,这个视频可以说是B站播放全网第一的自动化测试教程,同时在线人数到达1000人,并且还有笔记可以领取及各路大神技术交流:798478386   

【已更新】B站讲的最详细的Python接口自动化测试实战教程全集(实战最新版)_哔哩哔哩_bilibili【已更新】B站讲的最详细的Python接口自动化测试实战教程全集(实战最新版)共计200条视频,包括:1、接口自动化之为什么要做接口自动化、2、接口自动化之request全局观、3、接口自动化之接口实战等,UP主更多精彩视频,请关注UP账号。icon-default.png?t=N7T8https://www.bilibili.com/video/BV17p4y1B77x/?spm_id_from=333.337

接下来,根据这几个表我们来看在 django 中如何编写对应的模型,以及在数据库层面的处理。

多对一

在 django 中要表达多对一的关系需要使用 django.db.models.ForeignKeyField 字段。上图中,报名表和学生表,课程表,课程顾问表是多对一的关系,模型代码如下:


from django.db import models

class Student(models.Model): # 必须继承
  
    name = models.CharField('姓名', max_length=20, help_text='姓名')
    age = models.SmallIntegerField('年龄', null=True, blank=True, help_text='年龄')
    sex = models.SmallIntegerField('性别', default=1, help_text='性别')
    qq = models.CharField('qq号码', max_length=20, null=True, blank=True, unique=True, help_text='qq号码')
    phone = models.CharField('手机号码', max_length=20, null=True, blank=True, unique=True, help_text='手机号码')
    c_time = models.DateTimeField('创建时间', auto_now_add=True)

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'tb_student'   # 设置创建表示的表名
        verbose_name = '学生信息'
        verbose_name_plural = verbose_name  # django admin中显示模型的说明


class Salesman(models.Model):
    # GroupChoice = [
    #     ('电销', '电销'),
    #     ('网销', '网销'),
    #     ('班主任', '班主任'),
    # ]
    class GroupChoice(models.TextChoices):
        A = '电销', '电销'
        B = '网销', '网销'
        C = '班主任', '班主任'

    name = models.CharField('姓名', max_length=24, help_text='姓名')
    age = models.SmallIntegerField('年龄', null=True, blank=True, help_text='年龄')
    sex = models.SmallIntegerField('性别', default=1, help_text='性别')
    group = models.CharField('销售组', help_text='销售组', max_length=24, choices=GroupChoice.choices, default=GroupChoice.A )
    # group = models.CharField('销售组', help_text='销售组', max_length=24, choices=GroupChoice, default='电销')

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'tb_salesman'
        verbose_name = '课程顾问表'
        verbose_name_plural = verbose_name

class Course(models.Model):
    name = models.CharField('课程名称', max_length=24, help_text='课程名称', unique=True)
    price = models.IntegerField('价格', help_text='课程价格')
    period = models.SmallIntegerField('课时', help_text='课时,以小时为单位')

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'tb_course'
        verbose_name = '课程表'
        verbose_name_plural = verbose_name


class Entry(models.Model):
    student = models.ForeignKey(Student, verbose_name='学生', help_text='报名学生', on_delete=models.PROTECT)
    salesman = models.ForeignKey('Salesman', verbose_name='课程顾问', help_text='课程顾问', on_delete=models.PROTECT)
    course = models.ForeignKey(Course, verbose_name='课程', help_text='报名课程', on_delete=models.PROTECT, db_constraint=False)
    c_time = models.DateTimeField('报名时间', auto_now_add=True, help_text='报名时间')

    def __str__(self):
        return '{}-{}'.format(self.student.name, self.salesman.name)

    class Meta:
        db_table = 'tb_entry'
        verbose_name = '报名表'
        verbose_name_plural = verbose_name

定义 ForeignKeyField 字段时有如下注意事项

  1. 一般外键字段定义在多的一方

  2. 外键字段的第一个参数是一个位置参数,就是要关联的模型,可以是模型类本身,也可是字符串形式的导入路径(当引用其他应用的模型,和引入后定义的模型时很有用)

  3. 在数据库层面,django 会在字段名的后面附件 _id 来创建数据库列名。例如上面例子中的 Entry 模型的数据库表将有一个 student_id 列,然后为这个列创建一个外键约束,被引用的表为 tb_student,被引用的字段为 id.

    级联操作

    当一个由 ForeignKey 引用的对象被删除时,django 将模拟 on_delete 参数指定的 SQL 约束行为。

    注意是模拟,在数据库层面创建的外键的级联操作是 restrict。

  4. 注意:有时候为了效率,在数据库不会创建外键,而是通过代码逻辑来保证数据的完整性。在 django 中可以通过 ForeignKey 字段中指定 db_constraint=False 来控制不创建外键约束。所以上图中没有 course_id 的外键。

级联操作

当一个由 ForeignKey 引用的对象被删除时,django 将模拟 on_delete 参数指定的 SQL 约束行为。

注意是模拟,在数据库层面创建的外键的级联操作是 restrict。

 on_delete 的可能值有:

  • CASCADE

    • 级联删除

  • PROTECT

    • 通过引发 ProtectedErro 防止删除被引用字段

  • RESTRICT

    • 通过引发 RestrictErro 防止删除被引用字段

  • SET_NULL

    • 设置外键为空,只有当 null=true 才可以

ForeignKey 字段必须指定 on_delete。

一对一

在 django 中要表达一对一的关系需要使用 django.db.models.OneToOneField 字段,概念上类似于 ForeignKey 与 unique=True 的组合。

在 crm 中,学生详情表与学生表就是一个一对一的关系,创建模型如下:


class StudentDetail(models.Model):
    STATION_CHOICES = [
        ('功能测试工程师', '功能测试工程师'),
        ('自动化测试工程师', '自动化测试工程师'),
        ('测试开发工程师', '测试开发工程师'),
        ('测试组长', '测试组长'),
        ('测试经理', '测试经理'),
    ]

    class SalaryChoice(models.TextChoices):
        FIRST = '5000以下', '5000以下'
        SECOND = '5000-10000', '5000-10000'
        THIRD = '10000-15000', '10000-15000'
        FOURTH = '15000-20000', '15000-20000'
        FIFTH = '20000以上', '20000以上'

    student = models.OneToOneField(Student, verbose_name='学生', on_delete=models.CASCADE, help_text='学生')
    city = models.CharField('所在城市', max_length=24, help_text='所在城市', null=True, blank=True)
    company = models.CharField('任职公司', max_length=48, help_text='任职公司', null=True, blank=True)
    station = models.CharField('岗位', max_length=24, help_text='岗位', choices=STATION_CHOICES, default='功能测试工程师' )
    salary = models.CharField('薪资', max_length=24, help_text='薪资区间', choices=SalaryChoice.choices, default=SalaryChoice.FIRST)

    def __str__(self):
        return self.student.name

    class Meta:
        db_table = 'tb_student_detail'
        verbose_name = '学生详情表'
        verbose_name_plural = verbose_name

多对多

在 django 中要表达多对多的关系需要使用 django.db.models.ManyToManyField 字段,例如 Pizza 含有多种 Topping(配料),一种配料也可能存在于多个 pizza 中,每个 pizza 含有多种 topping 的关系,可以用下面的模型来表示:

class Topping(models.Model):
    name = models.CharField('名称', max_length=24)


class Pizza(models.Model):
    name = models.CharField('名称', max_length=24)
    toppings = models.ManyToManyField(Topping)

 定义 ManyToManyField 字段时有如下注意事项

  1. 建议设置多对多字段名为一个复数名词,表示所要管理的模型对象的集合。

  2. 多以多对多关联的两个模型,可以在任何一个模型中添加多对多字段,但是只能选择一个模型设置,即不能在两个模型里都添加。

  3. 一般来讲,应该把多对多字段放到需要在表单中编辑的对象里。跟业务相关,具体情况具体对待。

  4. 在数据库层面,django 会自动创建一张中间表来表示多对多的关系。默认情况下,这个表名是使用多对多字段的名字和包含它的模型名生成(上面的例子,会生成 pizza_toppins),然后包含两个字段,分别是以两个关系模型的名字和 _id 组成(pizza_id,topping_id),并创建外键引用对应的表的 id。

图片

图片

自定义中间表

当表示多对多关系的中间表需要包含其他字段的时候,需要自定义中间表,然后再定义多对多字段的时候,通过 through 参数指定第三张表。

例如 crm 中的学生表和课程表的关系,通过报名表来表达,其中还包含了销售,创建时间字段。注意:创建学生,或者是创建课程的时候,都不需要去编辑彼此,这个时候建立多对多字段,主要是为了查询方便。然后通过课程查包名的学生表业务上可能用的更多,所以把多对多的字段定义在课程表中,代码如下:

class Course(models.Model):
    name = models.CharField('课程名称', max_length=24, help_text='课程名称', unique=True)
    price = models.IntegerField('价格', help_text='课程价格')
    period = models.SmallIntegerField('课时', help_text='课时,以小时为单位')
    students = models.ManyToManyField(Student, through='Entry', verbose_name='学生', help_text='包名课程的学生')

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'tb_course'
        verbose_name = '课程表'
        verbose_name_plural = verbose_name
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Django ORM 框架中的表关系,你真的弄懂了吗? 的相关文章

随机推荐

  • springboot-单点登录

    单点登录 第一节 简介 1 1 什么是单点登陆 单点登录 Single Sign On 简称为 SSO 是目前比较流行的企业业务整合的解决方案之一 SSO的定义是在多个应用系统中 用户只需要登录一次就可以访问所有相互信任的应用系统 较大的企
  • 2023年美赛C题思路复盘

    论文标题 Riddle of Wordle Mining the Secret of Number Scores Solution Words Wordle之谜 挖掘数字得分和解字词的秘密 文章目录 前言 一 题目重述 拟解决的问题 我们的
  • mybatis异常:Could not find result map XXXX

    在使用mybatis框架 报这个错误时 是mapper文件中 查询语句的返回类型写错了 即该用resultType用成了resultMap 如果你要返回基本类型 或者返回已存在的pojo对象 用resultType修饰 如果要使用resul
  • Android ImageLoader用法总结

    前言 imageloader作为一个开源的框架被广泛的使用 尤其对于新手而言 更是如此 本人在项目中每当用到imageloader的时候都是从网上百度然后复制粘贴 现在觉得那样没得意义 因此写此博客 当作以后发复习资料 同时稍微研究下ima
  • springboot依赖

  • 三层架构:界面层、业务逻辑层、数据访问层

    进行用户的插入操作 1 spring会接管三层架构中的那些对象的创建 2 非spring接管下的三层项目构建 从下往上 1 实体类 com bjpowernode pojo Users package com bjpowernode poj
  • 【web开发】扩展与全面接管springmvc

    1 扩展springmvc 方法 编写一个配置类 Configuration 实现WebMvcConfigurer接口 不能标注 EnableWebMvc 特点 既保留了springboot所有的自动配置 也能用我们扩展的配置 示例 pom
  • 数据结构和算法(压缩矩阵和队列模拟)

    具体思路和分析放在末尾 稀疏矩阵的处理方法是 1 记录矩阵一共有几行几列 有多少个不同的值 2 把具有不同值的元素的行列和值记录都在一个小规模的矩阵中 从而缩小程序的规模 原始矩阵转换稀疏矩阵的思路 1 遍历原始的二维数组 得到有效数据的个
  • vue 的表单验证

    1 使用
  • zlib库各历史版本下载网址

    https zlib net fossils
  • Element——el-table给所有的row和column数据加上索引

    问题场景 开发中发现el table在使用cell style时无法获取row column的索引信息 解决思路 在其他可以获取到行列索引的方法中对行列数据添加索引属性 解决方法 利用el table的cell class name配置 以
  • 【Detectron2】详解Detectron2中Mask RCNN的部分代码

    整体来说 Backbone RPN和Fast RCNN是三个相对独立的模块 Backbone对每张图片产生5 level的特征 并送入RPN RPN对送入的特征 首先经过3x3卷积 随后用sibling 1x1卷积产生分类和bbox信息 分
  • Java类的封装

    目录 1 什么是类的封装 2 如何实现类的封装 1 private操作符 2 get与set 1 语法格式 2 封装演示 1 什么是类的封装 隐藏对象的属性和实现细节 仅对外提供公共访问方式 类的封装可以让使用者使用更加简单 数据也会更加安
  • 华为OD机试 Java 实现【密码验证合格程序】【牛客练习题 HJ20】,附详细解题思路

    一 题目描述 密码要求 长度超过8位 包括大小写字母 数字 其它符号 以上四种至少三种 不能有长度大于2的包含公共元素的子串重复 注 其他符号不含空格或换行 二 输入描述 一组字符串 三 输出描述 如果符合要求输出 OK 否则输出NG 四
  • Vcpkg 的安装与使用

    Vcpkg 的安装与使用 欢迎大家来我的博客逛逛 hauhau cn 需求 先确保你已经安装了 Windows7 及以上的版本 Git Visual Studio 2015 Update 3 及以上的版本 关于美化 Windows Term
  • 避免Flex RSL重复load 提高module加载性能

    本文编译后的补丁下载链接 http download csdn net source 1908278 多模块应用 每加载一个模块都会重复加载模块所使用的RSL 现在增加一个 private static var loadedRSLs Dic
  • golang实战-数字签名与认证

    一 数字签名 设想一个场景 Alice 给 Bob 发送了一段消息 明天我请你吃饭 该消息使用 Bob 的公钥加密 公钥加密才能确保消息被截获后也只有 Bob 本人能用自己的私钥解密 但是由于 Bob 的公钥可能其他人也有 Tom 也可以使
  • 搭建个人图床

    前言 在编写markdown文档的过程中 免不了插入一些图片 但是在分享的时候 这些带有图片资源的markdown就显得十分麻烦了 同时我也有写博客的习惯 hexo等用md转前端页面的项目在处理图片时也会遇到各种不舒服的操作 因此搭建自己的
  • 第7课 微信小程序实现图片搜索器案例:

    第7课 微信小程序实现图片搜索器案例 效果图如下 手机运行效果 来我们来看看代码的全局样式 首先在app json内的pages把需要的index1 index2页面注册 之后我们只需写index1 index2两个页面 即可实现图片搜索小
  • Django ORM 框架中的表关系,你真的弄懂了吗?

    Django ORM 框架中的表关系 为了说清楚问题 我们设计一个 crm 系统 包含五张表 1 tb student 学生表 2 tb student detail 学生详情表 3 tb salesman 课程顾问表 4 tb cours