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

 

 

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 字段。上图中,报名表和学生表,课程表,课程顾问表是多对一的关系,模型代码如下:

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.

图片

图片

  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 中,学生详情表与学生表就是一个一对一的关系,创建模型如下:

​​​​​​​

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

 

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

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

相关文章

Vue2.0+Element实现日历组件

(壹)博主介绍 🌠个人博客: 尔滨三皮⌛程序寄语:木秀于林,风必摧之;行高于人,众必非之。 (贰)文章内容 1、安装依赖 npm install moment2.29.4 --savenpm install lunar0.0.3 --savenpm install lunar-java…

C语言数据结构——链表

(图像由AI生成) 0.前言 在计算机科学中,数据结构是存储和组织数据的一种方式,它不仅影响数据的存储,也影响数据的检索和更新效率。C语言,作为一种经典的编程语言,提供了灵活的方式来处理数据…

OkHttp完全解读

一,概述 OkHttp作为android非常流行的网络框架,笔者认为有必要剖析此框架实现原理,抽取并理解此框架优秀的设计模式。OkHttp有几个重要的作用,如桥接、缓存、连接复用等,本文笔者将从使用出发,解读源码&am…

iOS 文件分割保存加密

demo只是验证想法,没有做很多异常处理 默认文件是大于1KB的,对于小于1KB的没有做异常处理demo中文件只能分割成2个,可以做成可配置的N个文件分割拼接还可以使用固定的二进制数据,拼接文件开头或结尾 不论哪种拼法,目的…

牛客周赛30

思路:先把x, y除以最大公约数变成最小值,然后同时乘以倍数cnt,只记录两个数都在[l,r]间的倍数。 代码: int gcd(int a,int b){return b ? gcd(b, a % b) : a; }void solve(){int x, y, l, r;cin >> x >> y >>…

两两交换链表中的结点---链表OJ

https://leetcode.cn/problems/swap-nodes-in-pairs/description/?envType=study-plan-v2&envId=top-100-liked 1、递归 创建newhead = head->next,然后将head->next->next作为递归参数,返回值用head->next接收;递归结束条件是:没有结点或者只有一…

Python代码耗时统计

time模块 在代码执行前后各记录一个时间点,两个时间戳相减即程序运行耗时。这种方式虽然简单,但使用起来比较麻烦。 time.time() 函数返回的时间是相对于1970年1月1日的秒数 import timestart time.time() time.sleep(1) end time.time() print(f&…

洛谷 P2150 [NOI2015] 寿司晚宴

P2150 [NOI2015] 寿司晚宴 约定: n ≤ 500 n \leq 500 n≤500 题意 给定 2 → n 2 \rightarrow n 2→n 共 n − 1 n-1 n−1 个数字,现在两个人想分别取一些数字(不一定全取完),如果他们两人取的数字中存在&#xf…

05. java线程基础

05. java线程基础 01. 线程相关概念 1. 程序 ​ 是为完成特定任务、用某种语言编写的一组指令的集合。简单来说:就是我们写的代码 2. 进程 进程是指运行中的程序,比如我们使用微信,就启动了一个进程,操作系统会为该进程分配内…

【代码随想录】LC 242. 有效的字母异位词

文章目录 前言一、题目1、原题链接2、题目描述 二、解题报告1、思路分析2、时间复杂度3、代码详解 前言 本专栏文章为《代码随想录》书籍的刷题题解以及读书笔记,如有侵权,立即删除。 一、题目 1、原题链接 242. 有效的字母异位词 2、题目描述 二、解题…

一文理清楚-Docker 容器如何工作

Docker 容器如何工作 集装箱什么是虚拟机?虚拟化如何运作?什么是容器?什么是 Docker?总结 五星上将麦克阿瑟曾经说过:在docker面前,虚拟机就是个弟弟 集装箱 《盒子:集装箱如何让世界变得更小&…

剑指offer——删除链表的节点

题目描述&#xff1a;给定单向链表的头指针和一个要删除的节点的值&#xff0c;定义一个函数删除该节点。返回删除后的链表的头节点。 数据范围&#xff1a; 0 <链表节点值 < 10000 0 <链表长度 < 10000 示例1&#xff1a; 输入&#xff1a;{2,5,1,9}&#xff…

1.28寒假集训

A: 解题思路&#xff1a; 移项就好v mv / (M - m) 下面是c代码&#xff1a; #include<iostream> using namespace std; int main() {int t;double M,m,v;cin >> t;while(t ! 0){cin >> M >> m >> v;printf("%.2lf\n",(m * v) / (M…

数据库之 基础概念、安装mysql、sql语句基础

数据库之 基础概念、安装mysql、sql语句基础 【一】存储数据的演变过程&#xff1a; 文件存储&#xff1a; 初始阶段随意存放数据到文件&#xff0c;格式任意。目录规范引入&#xff1a; 软件开发使用目录规范&#xff0c;限制数据位置&#xff0c;建立专门文件夹。本地数据存…

Linux报 “no route to host” 异常 ping: sendmsg: No route to host

公司有台服务器迁移机房后跟另一台服务器相互ping不通&#xff0c;但是两台服务器都能上网能ping其他机器&#xff0c;其他机器都能ping通这两台服务器。检查两台服务器没有防火墙规则拦截&#xff0c;交换机上也没检查到acl过滤。 下图是迁移机房的服务器ping截图 下图是nfs服…

分布式空间索引了解与扩展

目录 一、空间索引快速理解 &#xff08;一&#xff09;区域编码 &#xff08;二&#xff09;区域编码检索 &#xff08;三&#xff09;Geohash 编码 &#xff08;四&#xff09;RTree及其变体 二、业内方案选取 三、分布式空间索引架构 &#xff08;一&#xff09;PG数…

腾讯云幻兽帕鲁4核16G14M服务器性能测评和价格

腾讯云幻兽帕鲁服务器4核16G14M配置&#xff0c;14M公网带宽&#xff0c;限制2500GB月流量&#xff0c;系统盘为220GB SSD盘&#xff0c;优惠价格66元1个月&#xff0c;277元3个月&#xff0c;支持4到8个玩家畅玩&#xff0c;地域可选择上海/北京/成都/南京/广州&#xff0c;腾…

通讯录项目(终)

Start And Stick 上一期我们将通讯录的项目的基本功能已经实现&#xff0c;这一篇文章我们将对通讯录进行完善。 目录 Start And Stick 上期回顾&#xff1a; 上期必要代码&#xff1a; 数据打印&#xff1a; 代码讲解&#xff1a; 头部插入数据&#xff1a; 代码讲解&…

27.1K Star,优雅的JSON 数据可视化工具

Hi&#xff0c;骚年&#xff0c;我是大 G&#xff0c;公众号「GitHub指北」会推荐 GitHub 上有趣有用的项目&#xff0c;一分钟 get 一个优秀的开源项目&#xff0c;挖掘开源的价值&#xff0c;欢迎关注。 想自己之前做 APP 开发会访问后端数据&#xff0c;这个数据就是 JSON …

【网络基础】网络协议传输层UDP和TCP

UDP 解包和分用 解包&#xff08;解析数据包&#xff09; 捕获数据包&#xff1a;首先&#xff0c;接收端的网络栈捕获UDP数据包。检查目的端口&#xff1a;接收端检查数据包头部的目的端口&#xff0c;以确定哪个应用程序应该接收该数据包。验证校验和&#xff1a;接收端可能…