文章目录
- 前言
- 一、级联操作
- 二、一对多(多对一)的关联模型类设计
- 1、学生表模型类设计
- 2、渠道表模型类设计
- 三、多对多的关联模型类设计
- 1、课程表模型类设计
- 四、多对多的关联模型类设计(自定义中间表)
- 1、模型类设计
- 课程表模型类设计
- 报名表模型类设计
- 2、数据表字段
- 3、数据表结构
- 五、一对一的关联模型类设计
- 1、模型类设计
- 2、数据表结构
- 3、数据表字段
前言
已知学生表、学生详情表、课程表、报名表、渠道表;
其中学生表和学生详情表的关联关系为一对一;
学生表和课程表的关系为多对多;
学生表和渠道表的关系为一对多;
一、级联操作
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
与多对一不同,这个列还会创建一个唯一约束,形成一对一的关系,其他的级联操作与多对一一样