day6-django-表关系
表关系
多对一
在django中要表达多对一的关系需要使用django.db.models.ForeignKeyField字段。报名表,学生表,课程表,课程顾问表,是多对一的关系
1 class Student(models.Model): # 必须继承 2 """ 3 每个字段实例的名称,类属性名称,就是数据库字段的名称 4 """ 5 name = models.CharField(verbose_name="姓名", max_length=20, help_text="姓名") 6 age = models.SmallIntegerField("年龄", null=True, blank=True, help_text="年龄") 7 sex = models.SmallIntegerField("性别", default=1, help_text="性别") 8 qq = models.CharField("qq号码", max_length=20, null=True, blank=True, unique=True, help_text="qq号码") 9 phone = models.CharField("手机号码", max_length=20, null=True, blank=True, unique=True, help_text="手机号码") 10 channel = models.ForeignKey('Channel', on_delete=models.SET_NULL, null=True, verbose_name='渠道', help_text='渠道来源', 11 related_name='students') 12 c_time = models.DateTimeField("创建时间", auto_now_add=True) 13 14 def __str__(self): 15 """打印字符串输出的名字""" 16 return self.name 17 18 class Meta: 19 db_table = "tb_student" # 设置创建表的表名 20 verbose_name = "学生信息" 21 verbose_name_plural = verbose_name # django admin中显示模型的说明 22 ordering = ["age"] # 根据年龄排序从小到大 23 24 25 class Course(models.Model): 26 name = models.CharField("课程名称", max_length=24, help_text="课程名称", unique=True) 27 price = models.IntegerField("价格", help_text="课程价格") 28 period = models.SmallIntegerField("课时", help_text="课时,以小时为单位") 29 students = models.ManyToManyField(Student, through="Entry", verbose_name="学生", help_text="报名课程的学生") 30 31 def __str__(self): 32 return self.name 33 34 class Meta: 35 db_table = "tb_course" 36 verbose_name = "课程表" 37 verbose_name_plural = verbose_name 38 39 40 class Entry(models.Model): 41 student = models.ForeignKey(Student, verbose_name="学生", help_text="报名学生", on_delete=models.PROTECT) 42 course = models.ForeignKey('Course', verbose_name="课程", help_text="报名课程", on_delete=models.PROTECT) 43 c_time = models.DateTimeField("报名时间", auto_now_add=True, help_text="报名时间") 44 45 def __str__(self): 46 return "{}-{}".format(self.student.name, self.course.name) 47 48 class Meta: 49 db_table = "tb_entry" 50 verbose_name = "报名表" 51 verbose_name_plural = verbose_name 52 constraints = [ 53 UniqueConstraint(fields=['student', 'course'], name="unique_student_course") 54 ]
- 一般外键字段定义在多的一方
- 外键字段的第一个参数是一个位置参数,就是要关联的模型,可以是模型类本身,也可以是字符串形式的导入路径,当引用其它应用的模型,和引入后定义的模型时有用
- 外键引入models.ForeignKey(Student,....),models.ForeignKey('Student',....)关联表可以是一个模型类,也可以用字符串导入的方式
- 在数据库层面,django会在字段名的后面附加_id来创建数据库列名。例如上面例子中Entry模型的数据库将有一个student_id列,然后为这个列创建一个外键约束
- 注意:有时候为了效率,在数据库不会创建外键,而是通过代码逻辑来保证数据的完整性,可以通过ForeignKey字段中置顶db_constraint= False来控制不创建外键约束
级联操作
当一个由ForeignKey引用的对象被删除时,django将模拟on_delete参数指定的SQL约束行为
on_delete的可能值有:
- CASCADE:级联删除
- PROTECT:通过引发protectedErro防止删除被引用字段
- RESTRICT:通过引发restrictErro防止删除被引用字段
- SET_NULL:设置外键为空,只有当null=true才可以
注意:ForeignKey字段必须指定on_delete
一对一
在django中要表达一对一的关系需要使用django.db.models.OneToOneField字段,在crm中学生表和学生详情表就是一对一关系
1 class StudentDetail(models.Model): 2 # 下拉框定义方式一 3 STATION_CHOICES = [ 4 ("功能测试工程师", "功能测试工程师"), 5 ("自动化测试工程师", "自动化测试工程师"), 6 ("测试开发工程师", "测试开发工程师"), 7 ("测试组长", "测试组长"), 8 ("测试经理", "测试经理"), 9 ] 10 11 # 下拉框定义方式二 12 class SalaryChoice(models.TextChoices): 13 FIRST = "5000以下", "5000以下", 14 SECOND = "5000-10000", "5000-10000" 15 THIRD = "10000-15000", "10000-15000" 16 FOURTH = "15000-20000", "15000-20000" 17 FIFTH = "20000以上", "20000以上" 18 19 student = models.OneToOneField(Student, verbose_name="学生", on_delete=models.CASCADE, help_text="学生", null=True) 20 city = models.CharField("所在城市", max_length=24, help_text="所在城市", null=True, blank=True) 21 company = models.CharField("任职公司", max_length=48, help_text="任职公司", null=True, blank=True) 22 # 下拉框选择方式一 23 station = models.CharField("岗位", max_length=24, help_text="岗位", choices=STATION_CHOICES, default="功能测试工程师") 24 # 下拉框选择方式二 25 salary = models.CharField("薪资", max_length=24, help_text="薪资区间", choices=SalaryChoice.choices, default=SalaryChoice.SECOND) 26 27 def __str__(self): 28 return self.student.name 29 30 class Meta: 31 db_table = "tb_student_detail" 32 verbose_name = "学生详情表" 33 verbose_name_plural = verbose_name
多对多
在django中要表达一对一的关系需要使用django.db.models.ManyToManyField字段
- 建议设置多对多字段名为一个复数名词,表示所要管理的模型对象的集合。
- 多对多关联的两个模型,可以在任何一个模型中添加多对多字段,但是只能选择一个模型设置,即不能在两个模型里都添加。
- 一般来讲,应该把多对多字段放到需要在表单中编辑的对象里。跟业务相关,具体情况具体对待。
- 在数据库层面,django会自动创建一张中间表来表示多对多的关系。默认情况下,这个表名是使用多对多字段的名字和包含它的模型名生成(上面的例子,会生成
pizza_toppins
),然后包含两个字段,分别是以两个关系模型的名字和_id
组成(pizza_id,topping_id),并创建外键引用对应的表的id。
自定义中间表
当表示多对多关系的中间表需要包含其它字段的时候,需要自定义中间表,然后再定义多对多字段的时候,通过through参数指定第三张表,
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 7 def __str__(self): 8 return self.name 9 10 class Meta: 11 db_table = "tb_course" 12 verbose_name = "课程表" 13 verbose_name_plural = verbose_name