阅读本文前,博主强烈推荐大家花心思阅读:官方文档
基本概念引自官方文档,欲深入了解某一知识点,点击相应链接。
表关系有三个:
一对一:Student -> StuDetail
一对多:College -> Student
多对多:Student -> Course
class Student(models.Model):
name = models.CharField(max_length=30) # 主键可以省略,django 自动加上
age = models.IntegerField(default=0)
gender = models.SmallIntegerField(default=1)
c_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
# 一对多关系的模型表示,通过外键实现,参数1是关联的外表
# 数据库中对应的字段为:company_id
college = models.ForeignKey('College', on_delete=models.SET_DEFAULT, default='')
def __str__(self):
show_gender = '男'
if self.gender == 0:
show_gender = '女'
return 'id<%s>name[%s]gender(%s)age|%s|c_time:%s' % (
self.id, self.name, show_gender, self.age, self.c_time
)
# 项目开发中,把一张大表拆分为 一对一 关系的两张子表是常用手段
class StuDetail(models.Model):
qq = models.CharField(max_length=20, unique=True, default='')
#指定关系 第一个参数推荐使用 字符串类型的 ,毕竟python是解释型的语言
student = models.OneToOneField(Student, on_delete=models.CASCADE)
#on_delete参数指定的SQL约束的行为 ,后面会讲到
class College(models.Model):
name = models.CharField(max_length=20)
address = models.TextField(default='')
class Course(models.Model):
name = models.CharField('课程名称', max_length=20)
book = models.CharField(max_length=20)
student = models.ManyToManyField('Student', through='Enroll')
#示例中中间表需要其他字段,需要指定 Enroll
#多对多中间表
class Enroll(models.Model):
# student 和 course 是多对多关系,则在中间表建立两者外键
student = models.ForeignKey('Student', on_delete=models.CASCADE)
course = models.ForeignKey('Course', on_delete=models.CASCADE)
pay = models.FloatField('缴费金额', default=0)
c_time = models.DateTimeField('报名时间', auto_now_add=True)
"_id"
字段名称来创建其数据库列名。上例中USer
表中会出现company_id
字段。.字段
即可。1.2 一对一
通过类 OneToOneField
实例创建映射
class models.OneToOneField():
def __init__(self, to, on_delete, to_field=None, **kwargs):
kwargs['unique'] = True
super().__init__(to, on_delete, to_field=to_field, **kwargs)
1.3 多对一
通过建立外键实现
class ForeignKey(ForeignObject):
def __init__(self, to, on_delete, related_name=None, related_query_name=None,
limit_choices_to=None, parent_link=False, to_field=None,
db_constraint=True, **kwargs):
to
和 on_delete
,参数具体含义与一对一相同1.4 多对多
通过中间表中建立外键实现
1.1 的实例中,student
和 course
就是多对多关系。我们引入中间表 enroll
来连接他们。
当ForeignKey
删除外键引用的对象时,Django将模拟on_delete
参数指定的SQL约束的行为 。
#举个栗子
grade = models.ForeignKey(
Grade,
models.SET_NULL,
blank=True,
on_delete = models.CASCADE, # 表示级联删除
null = True
)
常用参数:(注–参数引自django官方文档,详细页面)
级联删除。Django模拟SQL约束ON DELETE CASCADE的行为,并删除包含ForeignKey的对象。Model.delete()
不会在相关模型上调用,但 会为所有已删除的对象发送pre_delete
和 post_delete
信号。
通过引发ProtectedError
子类来 防止删除引用的对象 django.db.IntegrityError
。
将ForeignKey其设置为null
; ForeignKey必须设置null=true
值 。
将ForeignKey其设置为默认值; ForeignKey必须设置的默认值 。
正向:通过定义外键的模型访问外键是正向。
In [1]: from book.models import Student, StuDetail, C
...: ollege, Course, Enroll
#因为多(Student)对一(College),所以先写入college记录
In [2]: c1 = College(name='飞院', address='德阳')
In [3]: c2 = College(name='川大', address='成都')
In [4]: c1.save()
In [5]: c2.save()
# 依据外键关联数据,外键字段赋值有两种
# 第一种指定对象
In [6]: s1 = Student(name='柯南', age=16 , gender=1, college=c1)
#college是student类中定义的外键属性
In [7]: s1.save()
#第二种直接赋值外键
In [8]: s2 = Student(name='步美', age=16, gender=0, college_id = c2.pk)
#给外键college_id赋值
In [9]: s2.save()
反向:假设模型A通过外键关联了模型B,那么通过B的实例b可以获取到A实例的管理器。这就是反向,反向获取的管理器名称默认为(模型名)A_set。
提前创建了College
模型实例
# c1 是主键为1的学校;c2,c3 是主键为2,3的学校
In [64]: c1 = College.objects.get(pk=1)
In [65]: c1
Out[65]: <College: <飞院>:[德阳]>
# 反向访问
In [95]: c1.student_set.all()
'''
在Foreignkey里面设置related_name='student',
这样就可以直接用自定义名称而不是默认_set的形式了
c1.student_set ->返回Student关联模型管理器,注意与Student.objects不同
管理器才有add,create, clear, remove方法
'''
添加数据
# create
In [40]: c1.student_set.create(name='元太', age=16, college=c1)
Out[40]: <Student: id<6>name[元太]gender(男)age|16|c_time:2019-03-18 05:26:58.097584+00:00>
# add
In [71]: s1 = Student(name='高木', college=c1, age=26)
In [72]: s2 = Student(name='白合', college=c2, age=26)
In [74]: s1.save() # 要先将实例刷入数据库
In [75]: s2.save()
In [76]: c1.student_set.add(s1, s2) #这里会解除s2 和 c2 的关联。
查看数据
In [11]: c1.student_set.filter(age=16)
Out[11]: <QuerySet [
<Student: id<1>name[步美]gender(女)age|16|c_time:2019-03-18 03:44:42.817462+00:00>, <Student: id<2>name[柯南]gender(男)age|16|c_time:2019-03-18 03:45:15.162859+00:00>, <Student: id<6>name[元太]gender(男)age|16|c_time:2019-03-18 05:26:58.097584+00:00>
]>
#模型管理器其他方法同样可用
'''
'all',
'annotate',
'count',
'defer',
'exclude',
'filter',
'first',
'only',
'order_by',
'reverse',
'''
修改数据
# update
In [13]: c1.student_set.update(college_id = 2)
Out[13]: 5
In [14]: c1.student_set.all()
Out[14]: <QuerySet []>
# c1对应的学生为空,此处创建,用于删除展示
In [15]: c1.student_set.update_or_create(college_id=1)
Out[15]:
(<Student: id<11>name[]gender(男)age|0|c_time:2019-03-18 13:45:15.737306+00:00>,
True)
# 此外还有 set 方法
In [17]:c1.student_set.set(s1, s2)
#set方法分两步:首先c1.student_set.clear();再调用c1.student.add(s1, s2)
删除数据
#delete 删除空白记录
In [24]: c1.student_set.all().delete()
Out[24]: (1, {'book.Enroll': 0, 'book.StuDetail': 0, 'book.Student': 1})
#clear 清空对应集合,删库跑路
In [24]: c1.student_set.clear() #打死不按回车键,只删除学校为c1的学生集合
Out [24]: ~~~~不知道结果,有人告诉我吗~~~
使用反向获取管理器时,必须用实例
# College.student_set
In [12]: smgr = College.student_set
In [13]: dir(smgr)
Out[13]:
['__class__',
'_get_set_deprecation_msg_params',
'field',
'rel',
'related_manager_cls',
'...'
]
# College().student_set
In [14]: smgr = College().student_set
In [15]: dir(smgr)
Out[15]:
[
'_update',
'add',
'aggregate',
'all',
'annotate',
'...'
]
定义模型时设置related_name 参数来覆盖默认名称.
class Student(models.Model):
college = models.ForeignKey(
'College',
related_name='student',
ondelete=models.CASCADE
)
可以通过外键__字段名
访问外表字段
# 取出学校名为 飞院 的学校的学生
In [50]: Student.objects.filter(college__name='飞院')
Out[50]: <QuerySet [
<Student: id<1>name[步美]gender(女)age|16|c_time:2019-03-18 03:44:42.817462+00:00>,
<Student: id<2>name[柯南]gender(男)age|16|c_time:2019-03-18 03:45:15.162859+00:00>
]>
#对应的 sql 语句
Out[51]:
'''
SELECT `book_student`.`id`, `book_student`.`name`, `book_student`.`age`, `book_student`.`gender`, `book_student`.`c_time`, `book_student`.`college_id`
FROM `book_student` INNER JOIN `book_college`
ON (`book_student`.`college_id` = `book_college`.`id`)
WHERE `book_college`.`name` = 飞院'
'''
多对多关系的使用类似于多对一关系的反向操作,同样是通过获取相关表的管理器来操作相关表。不同的是:多对多通过相关表的小写模型名来获取管理器,多对一通过小写模型名加_set
来获取。
举个栗子:
# Student 和 College是多对一
In [2]: co = College.objects.get(pk=1)
In [6]: type(co.student_set) #小写模型名加_set即student_set
Out[6]: django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager
# Student 和 Course 是多对多
In [3]: c1 = Course.objects.get(name='案情分析')
In [11]: type(c1.students)
Out[11]: django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager
多对多关系中,如果指定了中间表,set, add, remove方法都必须用中间表使用。
#创建实例
In [3]: c1 = Course.objects.get(name='案情分析')
In [4]: c2 = Course.objects.get(name='火遁')
In [5]: s1 = Student.objects.get(name='柯南')
In [6]: s2 = Student.objects.get(name='纳鲁淘')
In [12]: Enroll.objects.create(students=s1, courses=c1)
Out[12]: <Enroll: Enroll object (1)>
In [13]: e1 = Enroll.objects.get(students=s1)
In [15]: e2=Enroll(students=s2, courses=c2)
In [16]: e2.save()
#访问
In [19]: c1.students.all()
Out[19]: <QuerySet [<Student: id<1>name[柯南]gender(男)age|16|c_time:2019-03-20 15:25:09+00:00>]>
#是在模型 Course 中指定了 ManyToManyField ,所以c1 可以通过关联模型名访问。而s1只能通过模型名_set来访 问
In [20]: s1.course_set.all()
Out[20]: <QuerySet [<Course: <案情分析>:[名侦探柯南]>]>
增删改就是对中间表的简单操作,不做赘述,
关于查询,由于ManyRelatedMany
即多对多关系管理器的原因,可以通过关联表名称访问。
例如:Student 和 Course是多对多关系
In [21]: c2.students.all()
Out[21]: <QuerySet [<Student: id<8>name[纳鲁淘]gender(男)age|16|c_time:2019-03-20 15:28:16+00:00>]>
同样提供正反双向访问,访问时直接通过小写模型名即可,无需添加_set(引用一位帅比的原话:set是集合,一对一返回对象就可以了!!!)
一对一操作中访问的不再是相关表管理器,而是实例的相关对象。
下面直接举例,Student和StuDetail是一对一模型。
In [23]: sd1 = StuDetail.objects.create(qq='123456', student=s1)
#返回的是对象而非管理器
In [27]: sd1.student
Out[27]: <Student: id<1>name[柯南]gender(男)age|16|c_time:2019-03-20 15:25:09+00:00>
#同样返回对象,并且不需要后缀_set
In [28]: s1.studetail
Out[28]: <StuDetail: StuDetail object (1)>