news 2026/4/23 13:18:53

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Django ORM 框架中的表关系,你真的弄懂了吗?

Django ORM 框架中的表关系

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

1.tb_student 学生表

2.tb_student_detail 学生详情表

3.tb_salesman 课程顾问表

4.tb_course 课程表

5.tb_entry 报名表

表关系和字段如下图:

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

多对一

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

  1. from django.db import models

  2. class Student(models.Model): # 必须继承

  3. name = models.CharField('姓名', max_length=20, help_text='姓名')

  4. age = models.SmallIntegerField('年龄', null=True, blank=True, help_text='年龄')

  5. sex = models.SmallIntegerField('性别', default=1, help_text='性别')

  6. qq = models.CharField('qq号码', max_length=20, null=True, blank=True, unique=True, help_text='qq号码')

  7. phone = models.CharField('手机号码', max_length=20, null=True, blank=True, unique=True, help_text='手机号码')

  8. c_time = models.DateTimeField('创建时间', auto_now_add=True)

  9. def __str__(self):

  10. return self.name

  11. class Meta:

  12. db_table = 'tb_student' # 设置创建表示的表名

  13. verbose_name = '学生信息'

  14. verbose_name_plural = verbose_name # django admin中显示模型的说明

  15. class Salesman(models.Model):

  16. # GroupChoice = [

  17. # ('电销', '电销'),

  18. # ('网销', '网销'),

  19. # ('班主任', '班主任'),

  20. # ]

  21. class GroupChoice(models.TextChoices):

  22. A = '电销', '电销'

  23. B = '网销', '网销'

  24. C = '班主任', '班主任'

  25. name = models.CharField('姓名', max_length=24, help_text='姓名')

  26. age = models.SmallIntegerField('年龄', null=True, blank=True, help_text='年龄')

  27. sex = models.SmallIntegerField('性别', default=1, help_text='性别')

  28. group = models.CharField('销售组', help_text='销售组', max_length=24, choices=GroupChoice.choices, default=GroupChoice.A )

  29. # group = models.CharField('销售组', help_text='销售组', max_length=24, choices=GroupChoice, default='电销')

  30. def __str__(self):

  31. return self.name

  32. class Meta:

  33. db_table = 'tb_salesman'

  34. verbose_name = '课程顾问表'

  35. verbose_name_plural = verbose_name

  36. class Course(models.Model):

  37. name = models.CharField('课程名称', max_length=24, help_text='课程名称', unique=True)

  38. price = models.IntegerField('价格', help_text='课程价格')

  39. period = models.SmallIntegerField('课时', help_text='课时,以小时为单位')

  40. def __str__(self):

  41. return self.name

  42. class Meta:

  43. db_table = 'tb_course'

  44. verbose_name = '课程表'

  45. verbose_name_plural = verbose_name

  46. class Entry(models.Model):

  47. student = models.ForeignKey(Student, verbose_name='学生', help_text='报名学生', on_delete=models.PROTECT)

  48. salesman = models.ForeignKey('Salesman', verbose_name='课程顾问', help_text='课程顾问', on_delete=models.PROTECT)

  49. course = models.ForeignKey(Course, verbose_name='课程', help_text='报名课程', on_delete=models.PROTECT, db_constraint=False)

  50. c_time = models.DateTimeField('报名时间', auto_now_add=True, help_text='报名时间')

  51. def __str__(self):

  52. return '{}-{}'.format(self.student.name, self.salesman.name)

  53. class Meta:

  54. db_table = 'tb_entry'

  55. verbose_name = '报名表'

  56. verbose_name_plural = verbose_name

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

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

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

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

  1. 注意:有时候为了效率,在数据库不会创建外键,而是通过代码逻辑来保证数据的完整性。在 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 中,学生详情表与学生表就是一个一对一的关系,创建模型如下:

  1. class StudentDetail(models.Model):

  2. STATION_CHOICES = [

  3. ('功能测试工程师', '功能测试工程师'),

  4. ('自动化测试工程师', '自动化测试工程师'),

  5. ('测试开发工程师', '测试开发工程师'),

  6. ('测试组长', '测试组长'),

  7. ('测试经理', '测试经理'),

  8. ]

  9. class SalaryChoice(models.TextChoices):

  10. FIRST = '5000以下', '5000以下'

  11. SECOND = '5000-10000', '5000-10000'

  12. THIRD = '10000-15000', '10000-15000'

  13. FOURTH = '15000-20000', '15000-20000'

  14. FIFTH = '20000以上', '20000以上'

  15. student = models.OneToOneField(Student, verbose_name='学生', on_delete=models.CASCADE, help_text='学生')

  16. city = models.CharField('所在城市', max_length=24, help_text='所在城市', null=True, blank=True)

  17. company = models.CharField('任职公司', max_length=48, help_text='任职公司', null=True, blank=True)

  18. station = models.CharField('岗位', max_length=24, help_text='岗位', choices=STATION_CHOICES, default='功能测试工程师' )

  19. salary = models.CharField('薪资', max_length=24, help_text='薪资区间', choices=SalaryChoice.choices, default=SalaryChoice.FIRST)

  20. def __str__(self):

  21. return self.student.name

  22. class Meta:

  23. db_table = 'tb_student_detail'

  24. verbose_name = '学生详情表'

  25. verbose_name_plural = verbose_name

多对多

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

  1. class Topping(models.Model):

  2. name = models.CharField('名称', max_length=24)

  3. class Pizza(models.Model):

  4. name = models.CharField('名称', max_length=24)

  5. toppings = models.ManyToManyField(Topping)

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

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

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

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

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

自定义中间表

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

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

  1. class Course(models.Model):

  2. name = models.CharField('课程名称', max_length=24, help_text='课程名称', unique=True)

  3. price = models.IntegerField('价格', help_text='课程价格')

  4. period = models.SmallIntegerField('课时', help_text='课时,以小时为单位')

  5. students = models.ManyToManyField(Student, through='Entry', verbose_name='学生', help_text='包名课程的学生')

  6. def __str__(self):

  7. return self.name

  8. class Meta:

  9. db_table = 'tb_course'

  10. verbose_name = '课程表'

  11. verbose_name_plural = verbose_name

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取

​​​​​​​

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 11:36:24

高危漏洞实战——10分钟捡了一个高危?只要比别人多走一小步

高危漏洞实战——10分钟捡了一个高危?只要比别人多走一小步 这个漏洞比较有意思,我甚至没有成功登录进去目标站点,这个站有什么功能还不清楚,就花了大概10来20分钟吧,喜提一个高危(虽然是个边缘站&#xf…

作者头像 李华
网站建设 2026/4/23 0:40:10

Open-AutoGLM安全架构设计揭秘:3步实现敏感数据跨域安全流转

第一章:Open-AutoGLM 跨应用数据安全机制在分布式系统架构中,Open-AutoGLM 作为支持跨应用数据交互的智能模型引擎,其核心安全机制设计直接影响数据的完整性与机密性。为确保不同应用间的数据流通不被恶意截取或篡改,系统采用多层…

作者头像 李华
网站建设 2026/4/23 10:50:00

5、超格拉斯曼代数作为量子空间的深入探究

超格拉斯曼代数作为量子空间的深入探究 1. 基础概念与正交性分析 在超格拉斯曼代数的研究中,我们首先关注基 (A_W)。通过一系列计算,我们得到了如下重要结果: [ \begin{align } \langle\theta^a\theta^b, \theta^c\theta^d\rangle_w&=\int\int d\theta: (\theta^a…

作者头像 李华
网站建设 2026/4/23 10:49:43

7、考克斯特多面体沿镜面滚动的研究

考克斯特多面体沿镜面滚动的研究 1. 滚动示例 正二十面体群 (H_3) :该群由关于正二十面体相对棱中点连线的角平分线的反射生成。这些角平分线将 (\mathbb{R}^3) 分割成 120 个二面角为 (\frac{\pi}{2})、(\frac{\pi}{3})、(\frac{\pi}{5}) 的单纯锥。在图中,单纯锥被正二十…

作者头像 李华