【django】各种关联关系的模型类设计

在人生的道路上,不管是潇洒走一回,或者是千山独行,皆须是自己想走的路,虽然,有的人并不是很快就能找到自己的方向和道路,不过,只要坚持到底,我相信,就一定可以找到自己的路,只要找到路,就不必怕路途遥远了。

导读:本篇文章讲解 【django】各种关联关系的模型类设计,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

在这里插入图片描述


前言

已知学生表、学生详情表、课程表、报名表、渠道表;
其中学生表和学生详情表的关联关系为一对一;
学生表和课程表的关系为多对多;
学生表和渠道表的关系为一对多;

一、级联操作

on_delete=models.CASCADE:当父表数据删除时,相对应的从表数据会自动删除
on_delete=models.SET_NULL:当父表数据删除时,相对应的从表数据会自动设置为null
on_delete=models.PROTECT:当父表数据删除时,如果有相对应从表数据会抛出异常
on_delete=models.SET_DEFAULT:当父表数据删除时,相对应的从表数据会被自动设置为默认值,还需要额外指定default=True

注意:外键字段必须指定on_delete参数

二、一对多(多对一)的关联模型类设计

在django中要表达多对一的关系需要用到ForeignKey。

以学生模型类(Student)和渠道模型类(Channel)为案例:
学生表中定义外键

注意1:外键字段要定义在多的一方,这个字段名使用关系模型类的小写名字,所以Student模型中定义一个channel字段.

注意2:外键字段的第一个参数是一个位置参数,即需要关联的模型,可以是模型本身,也可以是模型的字符串形式的导路径(当引用后定义的模型的时候很有用),一般使用也可以是模型的字符串形式的导路径

注意3:在数据库层面,django会在外键字段名后附加_id来创建数据库列名,所以Student模型类的数据表将有一个channel_id的列,然后会为这个列创建一个外键约束,被引用的表为t_channel,被引用的字段为t_channel.id

在当前案例中,删除一个渠道时,渠道下的学生不应该删除,所以外键的级联操作为on_delete=models.SET_NULL

1、学生表模型类设计

默认每一个模型都会自动生成一个主键,所以id主键可以不写
如果写:id = models.AutoField(primary_key=True, verbose_name='主键',help_text='主键')

null=True和blank=True的区别:
null=True:表示的数据库层面的,代表数据库中该字段可以为空
blank=True:表示反序列化输入的时候可以为空
null和blank一般是成对出现的

class Student(models.Model):

    name = models.CharField(help_text='学生姓名',
                            verbose_name='学生姓名',
                            max_length=128)
    age = models.SmallIntegerField(help_text='年龄',
                                   verbose_name='年龄',
                                   null=True,
                                   blank=True)  #todo null和blank一般是成对出现的
    sex = models.SmallIntegerField(help_text='性别',
                              verbose_name='性别',
                              default=1),
    phone = models.CharField(help_text='手机号码',
                             verbose_name='手机号码',
                             unique=True,
                             max_length=11,
                             blank=True,
                             null=True)
    address=models.CharField(help_text='家庭住址',
                             verbose_name='家庭住址',
                             unique=True,
                             max_length=128,
                             blank=True,
                             null=True)
    create_time = models.DateTimeField(help_text='创建时间',
                                       verbose_name='创建时间',
                                       auto_now_add=True)

    channel=models.ForeignKey('Channel',
                              on_delete=models.RESTRICT,
                              null=True,
                              help_text='外键字段')  #不允许删除

    class Meta:
        db_table="t_student"        #指定生成数据库表的名称,如果不定义则自动生成crm_student
        verbose_name='学生表'
        verbose_name_plural=verbose_name    #在django-admin中模型的名字
        ordering=['id']     #排序

    def __str__(self):
        return self.name

2、渠道表模型类设计

class Channel(models.Model):
    name=models.CharField(help_text='渠道名称',
                          verbose_name='渠道名称',
                          unique=True,
                          max_length=128)

    class Meta:
        db_table='t_channel'
        verbose_name='渠道表'
        verbose_name_plural=verbose_name

    def __str__(self):
        return self.name


class Course(models.Model):
    name=models.CharField(help_text='课程名称',
                          verbose_name='课程名称',
                          unique=True,
                          max_length=128)
    students=models.ManyToManyField('Student',
                               help_text='报名学生',
                               verbose_name='报名学生')

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

    def __str__(self):
        return self.name

三、多对多的关联模型类设计

在django中要表达多对多的关系需要用到ManyToManyField字段
学生表和课程表是多对多的关系

注意1:多对多字段一般来说会定义在需要在表单中编辑的对象中,或者是业务中需要查询更多的模型中

在我们的案例中,编辑学生对象,或者编辑课程对象都不需要彼此,而在查询”报名的某个课程的学生有那些?”,这个需求会更多,所以把多对多的字段定义在课程表中

注意2:多对多关联字段里一般设置为关系模型的复数形式,表示关系模型的对象集,所以Course模型中的多对多字段名为students

注意3:多对多字段的第一个参数是位置参数,和外键字段一样

注意4:在数据库层面,多对多的字段并不会在数据库表中创建对应的字段,而是django会自动创建一个中间表来表示多对多的关联关系。
默认情况下这个中间表的表名使用创建多对多字段的模型的表名+_+多对多字段名,所以案例中的第三张中间表为t_course_students

注意5:第三张表分别包含2个字段,分别是两个关系模型的小写名+_id组成(student_id course_id),并且创建外键应用对应表的id,还会对这2个字段创建一个联合唯一的索引
在这里插入图片描述

1、课程表模型类设计

class Course(models.Model):
    name=models.CharField(help_text='课程名称',
                          verbose_name='课程名称',
                          unique=True,
                          max_length=128)
    students=models.ManyToManyField('Student',
                               help_text='报名学生',
                               verbose_name='报名学生')

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

    def __str__(self):
        return self.name

在这里插入图片描述

四、多对多的关联模型类设计(自定义中间表)

虽然django会自定义第三张中间表,但是不能提供额外的字段,如果中间表需要包含其他字段,就需要自定义中间表,然后在定义多对多字段的时候,通过through参数指定第三张中间表
students = models.ManyToManyField('Student',help_text='报名学生',verbose_name='报名学生',through='Entry')

Entry模型类中,字段student和course都是外键,分别引用Student模型、Course模型,表达这2个模型的多对多关系

1、模型类设计

课程表模型类设计

class Course(models.Model):
    name = models.CharField(help_text='课程名称',
                            verbose_name='课程名称',
                            unique=True,
                            max_length=128)
    students = models.ManyToManyField('Student',
                                      help_text='报名学生',
                                      verbose_name='报名学生',
                                      through='Entry')


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

    def __str__(self):
        return self.name

报名表模型类设计

class Entry(models.Model):
    student = models.ForeignKey('Student',
                                verbose_name='学生',
                                help_text='学生',
                                on_delete=models.PROTECT)
    course = models.ForeignKey('Course',
                               help_text='课程',
                               verbose_name='课程',
                               on_delete=models.PROTECT)
    create_time = models.DateTimeField(help_text='报名时间',
                                       verbose_name='报名时间',
                                       auto_now_add=True)

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

    def __str__(self):
        return f'{self.student.name}报名了{self.course.name}课程'

2、数据表字段

在这里插入图片描述

3、数据表结构

数据库迁移后django不会自动创建这两个外键的联合唯一索引

在这里插入图片描述
如果有需要,需要手动创建,在Meta类中添加如下代码

constraints=[UniqueConstraint(fields('student','course'),name='student_course_unique') ]

class Entry(models.Model):
    student = models.ForeignKey('Student',
                                verbose_name='学生',
                                help_text='学生',
                                on_delete=models.PROTECT)
    course = models.ForeignKey('Course',
                               help_text='课程',
                               verbose_name='课程',
                               on_delete=models.PROTECT)
    create_time = models.DateTimeField(help_text='报名时间',
                                       verbose_name='报名时间',
                                       auto_now_add=True)

    class Meta:
        db_table = 't_entry'
        verbose_name = '报名表'
        verbose_name_plural = verbose_name
        #todo 多字段联合唯一索引
        constraints=[
            UniqueConstraint(fields=('student','course'),name='student_course_unique')
        ]

    def __str__(self):
        return f'{self.student.name}报名了{self.course.name}课程'

数据表结构

在这里插入图片描述

五、一对一的关联模型类设计

在django中要表达多对多的关系需要用到OneToOneField字段

学生表和学生详情表的关系为一对一的关系,创建学习详情模型表如下

1、模型类设计

class StudentDetail(models.Model):
    STATION_CHOICES = [
        ('java', 'java'),
        ('python', 'python'),
        ('测试开发', '测试开发'),
        ('运维', '运维')
    ]
    SALARY_CHOICES = [
        ('5000以下', '5000以下'),
        ('10000-15000', '10000-15000'),
        ('15000-20000', '15000-20000'),
        ('20000以上', '20000以上')
    ]
    student = models.OneToOneField('Student',
                                   verbose_name='学生详情',
                                   help_text='学生详情',
                                   on_delete=models.CASCADE)
    city = models.CharField(help_text='所在城市',
                            verbose_name='所在城市',
                            max_length=128,
                            null=True,
                            blank=True)
    company = models.CharField(help_text='所在单位',
                               verbose_name='所在单位',
                               max_length=128,
                               null=True,
                               blank=True)
    station = models.CharField(help_text='岗位',
                               verbose_name='岗位',
                               max_length=128,
                               choices=STATION_CHOICES,
                               null=True,
                               blank=True,
                               default='测试开发')
    salary=models.CharField(help_text='薪资',
                            verbose_name='薪资',
                            null=True,
                            blank=True,
                            choices=SALARY_CHOICES,
                            max_length=20,
                            default='20000以上')

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

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

2、数据表结构

在数据库层面,会创建一个student_id的列,然后为这个列创建一个外键约束,引用表为t_student,字段为t_student.id

与多对一不同,这个列还会创建一个唯一约束,形成一对一的关系,其他的级联操作与多对一一样

在这里插入图片描述

3、数据表字段

在这里插入图片描述


在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/123148.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!