day 69-70 一对一 一对多 多对一联表查询

2023-05-16,,

day 69 orm操作之表关系,多对多,多对一

多对一/一对多,

多对多{类中的定义方法}

day69

1. 昨日内容回顾

    1. 单表增删改查
2. 单表查询API
返回QuerySet对象的:
1. .all()
2. .filter()
3. .values()
4. .values_list()
5. .exclude()
6. .order_by()
7. .reverse()
8. .distinct() 返回单个数据对象:
1. .first()
2. .last()
3. .get() 返回布尔值的:
1. .exists()
返回数字的:
1. .count() 3. 神奇的双下划线:
配合filter()来做各种查询。
1. id__gt=1 大于
2. id__lt=3 小于
3. id__in=[1,2,3] 多个id
4. id__range=[1,5] range循环
5. name__contains="s" 跨表包含
6. name__icontains="s" 包含大小写
7. name__startwith="a" 以开头
8. name__endswith="x" 以结尾
9. first_day__year="" 时间模块的应用
10. cname__isnull=True *! 空
4. Django日志配置 2. 今日内容
表之间的关系 1. 一对多
2. 一对一
如下所示:
class Student(model,Model):
sname =models.CharField(max_length=23,verbose_name='学生姓名)
the_class=models.ForeignKEy(to=Class,to_field='id',
on_delete=models.CASCADE, related_name='students')
detail = models.OneToOneField(to='StudentDetail', null=True) class StudentDetail(models.Model):
hetght=models.PositiveIntegerField()
weitht=models.PositiveIntegerField()
email=models.EmailField() 对应关系:
类 --> 表
类的属性 --> 字段
实例对象 --> 一条数据(数据行) 数据库层面:
外键 对应 另外一个表的一条记录
cid_id
ORM层面:
外键对应 另外一个表的一个对象 cid 就是班级对象
student_obj.cid_id --> 具体的值 为什么? Django把关系包装成一个具体的对象
cid 正向查找:
student_obj=models.Student.objects.first()
student_obj.detail.email
反向查找:
默认是表名_set.all()
detail_obj=models.StudentDetail.objects.get(id=1)
detail_obj.student.sname
如果在外键的字段中加了related_name属性,则按照该属性的值来反向查找 3. 多对多
老师 和 班级 通过第三张表关联 Teacher表
id tname 1 Alex
2 Egon
3 yuan Class表 id cname
1 1班
2 2班
3 3班 TeacherToClass id teacher_id class_id
1 1 1
2 1 2
3 1 3
4 2 1
5 2 3
6 1 1 多对多三种方式创建表格: 一般是第二种方法 第一种:(没有使用到ManyToMany的格式去创建,使用不到它内部封装好的一些便捷用法,但是可扩展性强便于理解)
class Course(models.Model):
id=models.AutoField(primary_key=True) # 主键
cname=models.CharField(max_length=20) # 班级名称
first_day=models.DateField() # 开班时间 class Teacher(models.Model):
tname=models.CharField(max_length=20) # 自定义三张表格,通过外键去关联上面两张表格
class Teacher2Class(models.Model):
teacher=models.ForeignKey(to='Teacher)
the_class=models.ForeignKey(to='Course') class Meta: # 这里是指定我们的表格里面的这两个字段不可以有重复的,不可以同时出现重复,也就是我们说的联合主键
unique_together=("teacher", 'the_class') 第二种方式:(我们这里只是定义了两个类,course和teacher,没有定义第三个类,但是我们运行程序的时候系统会自动给我们生成第三张表格出来用于建立我们的 多对多的关系,但是注意了,我们上面提到了我们并没有建立第三张表格,它系统给我们生成的,我们不能够对这个自动生成的表格进行操作,这就是不够遍历的地方,优势当然是我们在不适用第三张表格的前提下,另外两张表格的操作会更加便捷)
通过manyToManyField去创建
class Course(models.Model):
id=models.AutoField(primary_key=True)
cname=models.CharField(max_length=20)
first_day=models.DateField() class Teacher(models.Model):
tname=models.CharFirld(max_length=30)
cid=models.ManyToManyField(to='Course,relate_name='teachers) 第三种方式:(优劣自己体会)
通过外键和ManyToManyField创建
class Course(models.Model):
id=models.AutoField(primary_key=True)
cname=models.CharField(max_length=30)
first_day=models.DateField() class Teacher(models.Model):
tname=models.CharField(max_length=40)
# 通过manyToManyField和手动去创建第三张表格
cid=models.ManyToManyField(to='Class',through='Teacher2Class',through_fields=('teacher', 'the_class')) class Teacher2Class(models.Model):
teacher=models.ForeignKey(to='Teacher')
the_class=models.ForeignKey(to='Course') class Meta: # 建立联合主键
unique_together=('teacher', 'the_class') 多对多操作
正向查询(从老师表格查询班级表格)
teacher_obj=models.Teacher.objects.first()
teacher_obj.cid.all() # 查询该老师授课的所有班级 反向查询(由班级表格反向查询老师表格)
class_obj=models.Course.objects.first()
class_obj.teachers.all() #
此处用到的是related_name,如果不设置的话就用默认的表名_set 常用方法:
1create()
创建一个新的对象,保存对象,并将它添加到关联对象集中,返回新创建的对象
import datetime
teacher_obj.cid.create(cname='num9_class',first_day=datetime.datetime.now()) add()
把指定的models对象添加到关联对象集中
添加对象
class_obj=models.Course.objects.filter(id__lt=3)
models.Teacher.objects.first().cid.add(*class_obj) 添加id
models.Teacher.objects.first().cid.add(*[2,3,5]) set()
更新model对象的关联对象
teacher_obj=models.Teacher.objects.first()
teacher_obj.cid.set([2,3,4]) remove()
从关联对象集中移除执行的model对象
teacher_obj=models.Teacher.objects.first()
teacher_obj.cid.remove(3) clear()
从关联对象集中移除一切对象
teacher_obj=models.Teacher.objects.first()
teacher_obj.cid.clear() 4. 了不起的双下划线:
找学生名字里面带龙字的班级
sname__contains="龙"
models.Class.objects.filter(students__sname__contains="龙")
双下划线表示跨表 一对一关系: class Class(models.Model):
id = models.AutoField(primary_key=True)
cname = models.CharField(max_length=32, null=True)
first_day = models.DateField() def __str__(self):
return self.cname 为了日志的输出 class Student(models.Model):
id = models.AutoField(primary_key=True)
sname = models.CharField(max_length=32) cid = models.ForeignKey(to="Class", to_field="id", related_name="students") # 表里面:student_obj.cid_id=Class对象.id ; student_obj.cid=Class对象
# detail = models.ForeignKey(to="StudentDetail", unique=True)
detail = models.OneToOneField("StudentDetail", null=True) def __str__(self):
return self.sname class StudentDetail(models.Model):
# id = models.AutoField(primary_key=True)
# 正小整数
height = models.PositiveSmallIntegerField()
email = models.EmailField()
memo = models.CharField(max_length=128, null=True) =========================================== # 多对多
# 第一种
# 不能用Django ORM 多对多操作的语法
# class Teacher(models.Model):
# tname = models.CharField(max_length=32)
#
#
# class TeacherToClass(models.Model):
# tid = models.ForeignKey(to="Teacher")
# cid = models.ForeignKey(to="Class")
#
# class Meta:
# unique_together = ("tid", "cid") # 第二种
# 自动创建的第三张表,我没有类和它对应,也就是我不能通过ORM单独操作第三张表
class Teacher(models.Model):
tname = models.CharField(max_length=32)
cid = models.ManyToManyField("Class", null=False) # 第三种
# class Teacher(models.Model):
# tname = models.CharField(max_length=32)
# cid = models.ManyToManyField(to="Class", through="TeacherToClass", through_fields=("tid", "cid"))
#
#
# class TeacherToClass(models.Model):
# tid = models.ForeignKey(to="Teacher")
# cid = models.ForeignKey(to="Class")
#
# class Meta:
# unique_together = ("tid", "cid") 注意的点是我们在关联外键的时候可以直接指定表名即可,不用写上to,然后如果不指定字段的话orm会自动给我们关联到那个表格的id字段上,然后我们建立表格的时候如果不写id字段,不写主键的话,orm会自动给我们生成一个id字段并且自动指定i自动生成的这个id字段为主键 如果设置了related_name="students",反向查询时可直接使用students进行反向查询。 我们的related_name就是相当于我们的url路由设置里面的反向解析的原理,我们在查询数据的时候会用到这个功能,在反向解析里面我们直接使用别名就可以倒推出我们的url地址配置, 在表格关联外键的场景里面也是一样的,我们在查询的时候,通过一个表格去查他的外键的关联表格, 比如我关联你,那么我查询你的时候就是正向查询,而你查询我的话就属于反向查询,在反向查询里面我们就需要使用到了我们自定义的那个related_name值 那么我们的related_name放到哪里呢, 我们的A表格需要关联到B表格的一个字段作为外键,那么我们的related_name参数就放到我们的A表格的需要建立外键的那个字段里面,作为一个参数,以=赋值的形式放进去,要赋值给一个变量,用引号引起来的变量名,如下: class Student(models.Modes):
sname=models.CharField(max_length=20,verbose_name='学生姓名')
the_class=models.ForeignKey(to=Class, to_field='id', on_delete=models.CASCADE,
related_name='students')
detail=models.OneToOneField(to='StudnetDetail',null=True) class StudentDetail(models.Model):
height=models.PositiveIntegerField()
weight=models.PositiveIntegerField()
email=models.EmailField() 这里我们来针对一对多的具体事例进行一点解析: <table border="">
<thead>
<tr>
<th>#</th>
<th>ID</th>
<th>学生姓名</th>
<th>所在班级</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for student in student_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ student.id }}</td>
<td>{{ student.sname }}</td>
<td>{{ student.cid.cname }}</td>
{# 我们这里的student.cid.cname它的场景是我们在student表格里面建立了一个外键cid用它去关联我们的class表格里面的id字段#}
(# 我们这样写就是通过我student的这个外键的设置去找到了我们的class表格里面的cname字段并取到它的值,#)
{# 我们之所以可以这样操作是基于我们的orm里面的语法,通过类去创建表格然后根据类的属性去创建表格里面的字段,使用了面相对象编程的方式,极大地减少了代码的冗余#}
{# 我们的对应关系是 类---表 ;类的属性----字段 ; 实例化的对象---表格里面的一条数据行;#} <td>
<a href="{% url 'delete_student' student.id %}">删除</a>
{# <a href="{% url 'edit_class' %}?class_id={{ class.id }}">编辑</a>#}
<a href="{% url 'edit_student' student.id %}">编辑</a>
</td>
</tr>
{% endfor %} </tbody>
</table> 上面是前端部分,我们再来看看我们的后端代码是如何实现的,与之对应上, def student_list(request):   student_list=models.Student.objects.all()   return render(request, 'student_list.html',{"student_list": student_list} 具体解析部分: 这里的student是一个类,student_list是一个queryset(查询结果的集合,里面存储了一个或者多个对象),{%for student in student_list %}是从一个queryset中循环取出每个对象 student.cid是取出这一个对象的属性,(也就是类的属性,对应这个数据库中的这个表的字段,请参考上面注释部分的对应关系,),而此时这个cid是一个外键,对应着class那个类[也就是class那个表格]的id(数据库的id字段)对象,所以可以直接通过student.cid.cname去取出class表中对应的那一行数据的cname字段的值, 也就是说我们通过我们自己的表格的名字[student]加上.关联外键的字段名[cid]加上.想要查询的外键表格里面的字段[cname...]就可以实现我们在本表格里面去查找我们所关联外键的那个表格里面的字段.{这就有点类似于死记硬背了,固定格式,.....尽力去理解吧} 一对一 正向查询 student_obj=models.Student.objects.first() student_obj.detail.email 反向查询: detail_obj=models.StudentDetail.objects.get(id=2) detail_obj.student.sname # 我们一对一的时候反向查询只需要用得到对象.表名就可以拿到我们的结果了 多对多操作 正向查询 teacher_obj=models.Teacher.objects.first() teacher_obj.cid.all() 反向查询 class_obj=models.Class.objects.first() class_obj.teachers.all() # 此处我们设置了related_name,我们的related_name值是teachers,如果我们没有设置它的话就是用使用我们的表格名字Teacher_set.all()去取到所有的值 我们在这里把正向和反向总结一下,我们的一对一关系里面,和一对多的关系里面,要理清一个概念就是我们的关系里面谁要关联谁,这个是关键点,理清了之后我们建立表格的时候谁的字段里面需要添加外键,然后谁就是主动方,然后就可以分清被动方,这样我们顺着思路就可以理清了正向和反向的概念 在多对多关系里面我们无所谓谁关联谁,反正是一样的概念,但是我们依然需要分清正向和反向,正反向涉及到我们后续需要使用的方法,所以,跟上面一样我们的多对关系中,外键设定在谁的表格里面,那么谁就掌握了主动发方,谁就是正向,反之则是反向,所以,一句话总结,我们的一对一关系,一对多关系,多对多关系,都是需要区分正向和反向的.这样我们后续使用查询方法的时候便于区分哪些是正向哪些是反向的. 多对多关系的常用方法: create() 创建一个新的对象,保存对象,并将它添加到关联对象集中,返回新创建的对象 import datetime teacher_obj.cid.create(cname='9班',first_day=datetime.datetime.now()) add() 把指定的model对象添加到关联对象集中 添加对象 class_objs=models.Class.objects.filter(id__lt=3) # 这里使用到了双下线的方法,__lt,小于 models.Teacher.objects.first().cid.add(*class_objs) 添加id models.Teacher.objects.first().cid.add(*[2,4,3,1]) # 这里需要打散之后一个个添加 set() # 仅限于多对一或者多对多 更新model对象的关联对象 teacher_obj=models.Teacher.objects.first() teacher_obj.cid.set([2,1,4,5]) 示例: 我们在models里面把表格定义成多对多的关系: class Course(models.Model):
id = models.AutoField(primary_key=True)
cname = models.CharField(max_length=30)
first_day = models.DateField(null=True) def __str__(self):
return self.cname class Teacher(models.Model):
tname = models.CharField(max_length=20)
cid = models.ManyToManyField(to='Course', related_name='teachers') def __str__(self):
return self.tname
然后我们在演示set效果的时候,这样操作:
from api_app import models a=models.Teacher.objects.first() # 我们这里必须要是object对象才可以使用我们的set方法,如果是jqueryset则不具备这个方法, set的用法就是我们得到一个object对象然后我们通过我们的对象去.关联字段名.set([可迭代数据类型]) # 这里如果是字典的话,我们的修改只是取到了我们的key的值,那么我们的value里面的值就没有意义了,所以没有必要去浪费多余的内存空间,我们一般都是写成列表和元祖还有集合的形式,
a.cid.set(2,3,8) 我们的set方法是直接覆盖掉原来的值,不论我们之前有没有值,是什么值,都一刀切,直接替换掉,我们之前写的学生系统的增删改查,
在里面我们的 改 操作,里面包含了很多的可能性,有5种,增加,减少,更改,不更改,还有一个不记得了,总之都是封装到了我们的set用法里面了,
我们的add是直接在原来的基础上进行增加,不做修改,只管增加
remove() 从关联对象集中移除执行的model对象 teacher_obj=models.Teacher.objects.first() # 我们先取到这个对象, teacher_obj.cid.remove(5) # 然后再移除它 clear() 从关联对象中移除一切对象 teacher_obj=models.Teacher.objects.first() # 先得到一个对象 teacher_obj.cid.clear() # 我们再拿这个对象去到它的关联的外键的表格里面去清空那个表格里面的数据 我们在使用remove()或者clear()方法操作的时候,如果我们关联的那个表格---专业术语叫做对象集,对象集为空的话,必须要满足null=True, 也就是说我们如果清空了一组数据,而那组数据又在一开始设定的时候被设定为不能够为空,那么我们的程序是会报错的,你不能够清空一个不可以为空的字段值,也就是类属性,这是矛盾的,系统无法识别你的操作,不会执行操作的 对于所有类型的关联字段,add(),create(),remove()和clear(),set()都会马上更新数据库,换句话说,在关联的任何一端都不需要再调用save()方法, 好文要顶 已关注 收藏该文
dream-子皿
关注 - 10
粉丝 - 5
我在关注他 取消关注
0 0
« 上一篇:day 68 django 之api操作 | jQueryset集合与对象
» 下一篇:day70 cookie & session 前后端交互分页显示
posted @ 2018-01-23 16:24 dream-子皿 阅读(13) 评论(0) 编辑 收藏
刷新评论刷新页面返回顶部
发表评论
昵称:
小萝卜儿
评论内容:引用 粗体 链接 缩进 代码 图片 提交评论 退出 订阅评论
[Ctrl+Enter快捷键提交]
【推荐】超50万VC++源码: 大型工控、组态\仿真、建模CAD源码2018!
【腾讯云】小程序普惠节精美模板1元起
h3bpm0126
最新IT新闻:
· 董明珠投资银隆一年后:创始人退位 多位格力高管接手
· 阿里巴巴王坚:城市大脑打开下一代智能技术新大陆
· 网宿科技:未与腾讯就股权合作事项进行过洽谈 明日复牌
· BBC:中美英上演AI争霸“三国演义” 中国欲弯道超车
· 对话旅行青蛙日本团队:让青蛙去做我们做不到的事
» 更多新闻...
阿里云C2-1208
最新知识库文章:
· 领域驱动设计在互联网业务开发中的实践
· 步入云计算
· 以操作系统的角度述说线程与进程
· 软件测试转型之路
· 门内门外看招聘
» 更多知识库文章...
昵称:dream-子皿
园龄:3个月
粉丝:5
关注:10
已关注 -取消
< 2018年1月 >
日 一 二 三 四 五 六
31 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 1 2 3
4 5 6 7 8 9 10
搜索 找找看 谷歌搜索
常用链接
我的随笔
我的评论
我的参与
最新评论
我的标签
更多链接
我的标签
day3(1)
复习备用(1)
学习笔记(1)
随笔档案
2018年1月 (18)
2017年12月 (23)
2017年11月 (20)
2017年10月 (11)
阅读排行榜
1. pythonclass,day1-day2.(98)
2. day7 [id],[is],编码(59)
3. day4 字符串的操作(56)
4. day5 列表的增删改查(35)
5. 列表的增删改查(35)
Copyright ©2018 dream-子皿

结构大纲

<1> all():                 查询所有结果

<2> filter(**kwargs):      它包含了与所给筛选条件相匹配的对象

<3> get(**kwargs):         返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。

<4> exclude(**kwargs):     它包含了与所给筛选条件不匹配的对象

<5> values(*field):        返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列

<6> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列

<7> order_by(*field):      对查询结果排序

<8> reverse():             对查询结果反向排序

<9> distinct():            从返回结果中剔除重复纪录

<10> count():              返回数据库中匹配查询(QuerySet)的对象数量。

<11> first():              返回第一条记录

<12> last():               返回最后一条记录

<13> exists():             如果QuerySet包含数据,就返回True,否则返回False
models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值

models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in models.Tb1.objects.filter(name__contains="ven") # 获取name字段包含"ven"的
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 models.Tb1.objects.filter(id__range=[1, 3]) # id范围是1到3的,等价于SQL的bettwen and 类似的还有:startswith,istartswith, endswith, iendswith  date字段还可以:
models.Class.objects.filter(first_day__year=2017)
from django.db import models

# Create your models here.

一对一关系:

class Class(models.Model):
id = models.AutoField(primary_key=True)
cname = models.CharField(max_length=32, null=True)
first_day = models.DateField() def __str__(self):
return self.cname class Student(models.Model):
id = models.AutoField(primary_key=True)
sname = models.CharField(max_length=32) cid = models.ForeignKey(to="Class", to_field="id", related_name="students") # 表里面:student_obj.cid_id=Class对象.id ; student_obj.cid=Class对象
# detail = models.ForeignKey(to="StudentDetail", unique=True)
detail = models.OneToOneField("StudentDetail", null=True) def __str__(self):
return self.sname class StudentDetail(models.Model):
# id = models.AutoField(primary_key=True)
# 正小整数
height = models.PositiveSmallIntegerField()
email = models.EmailField()
memo = models.CharField(max_length=128, null=True) =====================================
# 多对多
# 第一种
# 不能用Django ORM 多对多操作的语法
# class Teacher(models.Model):
# tname = models.CharField(max_length=32)
#
#
# class TeacherToClass(models.Model):
# tid = models.ForeignKey(to="Teacher")
# cid = models.ForeignKey(to="Class")
#
# class Meta:
# unique_together = ("tid", "cid") # 第二种
# 自动创建的第三张表,我没有类和它对应,也就是我不能通过ORM单独操作第三张表
class Teacher(models.Model):
tname = models.CharField(max_length=32)
cid = models.ManyToManyField("Class", null=False) # 第三种
# class Teacher(models.Model):
# tname = models.CharField(max_length=32)
# cid = models.ManyToManyField(to="Class", through="TeacherToClass", through_fields=("tid", "cid"))
#
#
# class TeacherToClass(models.Model):
# tid = models.ForeignKey(to="Teacher")
# cid = models.ForeignKey(to="Class")
#
# class Meta:
# unique_together = ("tid", "cid")

注意的点是我们在关联外键的时候可以直接指定表名即可,不用写上to,然后如果不指定字段的话orm会自动给我们关联到那个表格的id字段上,

然后我们建立表格的时候如果不写id字段,不写主键的话,orm会自动给我们生成一个id字段并且自动指定i自动生成的这个id字段为主键

如果设置了related_name="students",反向查询时可直接使用students进行反向查询。

以下知识点

1 for 循环   {% for x in y %}  { % end for%}

2  序号自增{{ forloop.counter}}

3 通过get 取值  ?class_id={{ class.id }

4  url 别名反向解析  在html  {% url    "delete_student"%}

5 {% url 'delete_student' student.id %}  url传参数

<table border="">
<thead>
<tr>
<th>#</th>
<th>ID</th>
<th>学生姓名</th>
<th>所在班级</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for student in student_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ student.id }}</td>
<td>{{ student.sname }}</td>
<td>{{ student.cid.cname }}</td>
{# 我们这里的student.cid.cname它的场景是我们在student表格里面建立了一个外键cid用它去关联我们的class表格里面的id字段#}
(# 我们这样写就是通过我student的这个外键的设置去找到了我们的class表格里面的cname字段并取到它的值,#)
{# 我们之所以可以这样操作是基于我们的orm里面的语法,通过类去创建表格然后根据类的属性去创建表格里面的字段,使用了面相对象编程的方式,极大地减少了代码的冗余#}
{# 我们的对应关系是 类---表 ;类的属性----字段 ; 实例化的对象---表格里面的一条数据行;#} <td>
<a href="{% url 'delete_student' student.id %}">删除</a>
{# <a href="{% url 'edit_class' %}?class_id={{ class.id }}">编辑</a>#}
<a href="{% url 'edit_student' student.id %}">编辑</a>
</td>
</tr>
{% endfor %} </tbody>
</table>

一关于查询的实例

 1一对一  不用set

正向查询

student_obj=models.Student.objects.first()

student_obj.detail.email

反向查询:

detail_obj=models.StudentDetail.objects.get(id=2)

detail_obj.student.sname  # 我们一对一的时候反向查询只需要用得到对象.表名就可以拿到我们的结果了

多对多操作

正向查询

teacher_obj=models.Teacher.objects.first()

teacher_obj.cid.all()

反向查询

class_obj=models.Class.objects.first()

class_obj.teachers.all()

lass_obj.teachers_set_all()

# 此处我们设置了related_name,我们的related_name值是teachers,如果我们没有设置它的话就是用使用我们的表格名字Teacher_set.all()去取到所有的值

我们在这里把正向和反向总结一下,我们的一对一关系里面,和一对多的关系里面,要理清一个概念就是我们的关系里面谁要关联谁,这个是关键点,理清了之后我们建立表格的时候谁的字段里面需要添加外键, 一对一没有没有set的用法

然后谁就是主动方,然后就可以分清被动方,这样我们顺着思路就可以理清了正向和反向的概念

在多对多关系里面我们无所谓谁关联谁,反正是一样的概念,但是我们依然需要分清正向和反向,正反向涉及到我们后续需要使用的方法,所以,跟上面一样我们的多对关系中,外键设定在谁的表格里面,那么谁就掌握了主动发方,谁就是正向,反之则是反向,所以,一句话总结,我们的一对一关系,一对多关系,多对多关系,都是需要区分正向和反向的.这样我们后续使用查询方法的时候便于区分哪些是正向哪些是反向的.

多对多关系的常用方法:

create()   创建一个新的对象,保存对象,并将它添加到关联对象集中,返回新创建的对象

import datetime

teacher_obj.cid.create(cname='9班',first_day=datetime.datetime.now())

add()  可以添加对象 和多个id 注意要是列表需要打散前面加上*

把指定的model对象添加到关联对象集中

添加对象

class_objs=models.Class.objects.filter(id__lt=3)  #   这里使用到了双下线的方法,__lt,小于

models.Teacher.objects.first().cid.add(*class_objs)

添加id

models.Teacher.objects.first().cid.add(*[2,4,3,1])  # 这里需要打散之后一个个添加

set()  # 仅限于多对一或者多对多

更新model对象的关联对象

teacher_obj=models.Teacher.objects.first()

teacher_obj.cid.set([2,1,4,5])

示例:

我们在models里面把表格定义成多对多的关系:

class Course(models.Model):
id = models.AutoField(primary_key=True)
cname = models.CharField(max_length=30)
first_day = models.DateField(null=True) def __str__(self):
return self.cname class Teacher(models.Model):
tname = models.CharField(max_length=20)
cid = models.ManyToManyField(to='Course', related_name='teachers') def __str__(self):
return self.tname
然后我们在演示set效果的时候,这样操作:

from api_app import models

a=models.Teacher.objects.first()  # 我们这里必须要是object对象才可以使用我们的set方法,如果是jqueryset则不具备这个方法,

set的用法就是我们得到一个object对象然后我们通过我们的对象去.关联字段名.set([可迭代数据类型])   # 这里如果是字典的话,我们的修改只是取到了我们的key的值,那么我们的value里面的值就没有意义了,所以没有必要去浪费多余的内存空间,我们一般都是写成列表和元祖还有集合的形式,
a.cid.set(2,3,8)

我们的set方法是直接覆盖掉原来的值,不论我们之前有没有值,是什么值,都一刀切,直接替换掉,我们之前写的学生系统的增删改查,
在里面我们的 改 操作,里面包含了很多的可能性,有5种,增加,减少,更改,不更改,还有一个不记得了,总之都是封装到了我们的set用法里面了,
我们的add是直接在原来的基础上进行增加,不做修改,只管增加

remove()

从关联对象集中移除执行的model对象

teacher_obj=models.Teacher.objects.first()  # 我们先取到这个对象,

teacher_obj.cid.remove(5)  # 然后再移除它

clear()

从关联对象中移除一切对象

teacher_obj=models.Teacher.objects.first()  # 先得到一个对象

teacher_obj.cid.clear()  # 我们再拿这个对象去到它的关联的外键的表格里面去清空那个表格里面的数据

我们在使用remove()或者clear()方法操作的时候,如果我们关联的那个表格---专业术语叫做对象集,对象集为空的话,必须要满足null=True,

也就是说我们如果清空了一组数据,而那组数据又在一开始设定的时候被设定为不能够为空,那么我们的程序是会报错的,你不能够清空一个不可以为空的字段值,也就是类属性,这是矛盾的,系统无法识别你的操作,不会执行操作的

对于所有类型的关联字段,add(),create(),remove()和clear(),set()都会马上更新数据库,换句话说,在关联的任何一端都不需要再调用save()方法,

以下是练习

from app0001 import models

models.Student_list.objects.first()
(0.001) SELECT @@SQL_AUTO_IS_NULL; args=None
(0.001) SELECT `app0001_student_list`.`id`, `app0001_student_list`.`sname`, `app0001_student_list`.`cid_id`, `app0001_student_list`.`detail_id` FROM `app0001_student_list` ORDER BY `app0001_student_list`.`id` ASC LIMIT 1; args=()
<Student_list: 张思Linux 4期2019-12-23>

models.Student_list.objects.first().cid
(0.003) SELECT `app0001_student_list`.`id`, `app0001_student_list`.`sname`, `app0001_student_list`.`cid_id`, `app0001_student_list`.`detail_id` FROM `app0001_student_list` ORDER BY `app0001_student_list`.`id` ASC LIMIT 1; args=()
<Ban_list: Linux 4期2019-12-23>

models.Student_list.objects.first().cid.first_day
datetime.date(2019, 12, 23)
(0.001) SELECT `app0001_student_list`.`id`, `app0001_student_list`.`sname`, `app0001_student_list`.`cid_id`, `app0001_student_list`.`detail_id` FROM `app0001_student_list` ORDER BY `app0001_student_list`.`id` ASC LIMIT 1; args=()
(0.001) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list` WHERE `app0001_ban_list`.`id` = 19; args=(19,)

关于 related 关联的使用 如果没有使用related_name= xxx(这个表的别名,外键关联里面的)
必须使用 student_list_set first_class.student_list_set.all()
如果定义了 就可以换成这个参数查找

first_class = models.Ban_list.objects.first()

(0.001) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list` ORDER BY `app0001_ban_list`.`id` ASC LIMIT 1; args=()
(0.001) SELECT `app0001_student_list`.`id`, `app0001_student_list`.`sname`, `app0001_student_list`.`cid_id`, `app0001_student_list`.`detail_id` FROM `app0001_student_list` WHERE `app0001_student_list`.`cid_id` = 2 LIMIT 21; args=(2,)

first_class.student_list_set.all()   反向查询

(0.001) SELECT `app0001_student_list`.`id`, `app0001_student_list`.`sname`, `app0001_student_list`.`cid_id`, `app0001_student_list`.`detail_id` FROM `app0001_student_list` WHERE `app0001_student_list`.`cid_id` = 2 LIMIT 21; args=(2,)

<QuerySet [<Student_list: 王小Python 18期2023-10-19>]>

有了related_name=student 不需要用表名字和_set了 跨页面查找

from app0001 import models
first = models.Ban_list.objects.first()
(0.000) SELECT @@SQL_AUTO_IS_NULL; args=None
(0.001) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list` ORDER BY `app0001_ban_list`.`id` ASC LIMIT 1; args=()
first.student.all()   有了related_name=student 不需要用表名字和_set了 跨页面查找

<QuerySet [<Student_list: 王小Python 18期2023-10-19>]>

通过一个表产找属性 一个表的查询
models.Student_list.objects.first().sname
'张思'

(0.001) SELECT `app0001_student_list`.`id`, `app0001_student_list`.`sname`, `app0001_student_list`.`cid_id`, `app0001_student_list`.`detail_id` FROM `app0001_student_list` ORDER BY `app0001_student_list`.`id` ASC LIMIT 1; args=()

通过链表查找属性 一对一的跨表查询
models.Student_list.objects.first().detail.email
(0.001) SELECT `app0001_student_list`.`id`, `app0001_student_list`.`sname`, `app0001_student_list`.`cid_id`, `app0001_student_list`.`detail_id` FROM `app0001_student_list` ORDER BY `app0001_student_list`.`id` ASC LIMIT 1; args=()
(0.001) SELECT VERSION(); args=None
(0.002) SELECT `app0001_studentdetail`.`id`, `app0001_studentdetail`.`heigth`, `app0001_studentdetail`.`email`, `app0001_studentdetail`.`memo` FROM `app0001_studentdetail` WHERE `app0001_studentdetail`.`id` = 1; args=(1,)
'11@qq.com'

models.Student_list.objects.first().detail.memo
'football'

反向查找 通过关联得子表点.父表明 不用set 反向不用加上set 因为是一对一

models.StudentDetail.objects.first().student_list
(0.001) SELECT `app0001_studentdetail`.`id`, `app0001_studentdetail`.`heigth`, `app0001_studentdetail`.`email`, `app0001_studentdetail`.`memo` FROM `app0001_studentdetail` ORDER BY `app0001_studentdetail`.`id` ASC LIMIT 1; args=()
(0.001) SELECT VERSION(); args=None
(0.002) SELECT `app0001_student_list`.`id`, `app0001_student_list`.`sname`, `app0001_student_list`.`cid_id`, `app0001_student_list`.`detail_id` FROM `app0001_student_list` WHERE `app0001_student_list`.`detail_id` = 1; args=(1,)
(0.001) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list` WHERE `app0001_ban_list`.`id` = 19; args=(19,)
<Student_list: 张思Linux 4期2019-12-23>
反向查找 通过关联得子表点.父表明 不用set 反向不用加上set 因为是一对一
models.StudentDetail.objects.first().student_list.sname
(0.002) SELECT `app0001_studentdetail`.`id`, `app0001_studentdetail`.`heigth`, `app0001_studentdetail`.`email`, `app0001_studentdetail`.`memo` FROM `app0001_studentdetail` ORDER BY `app0001_studentdetail`.`id` ASC LIMIT 1; args=()
(0.001) SELECT `app0001_student_list`.`id`, `app0001_student_list`.`sname`, `app0001_student_list`.`cid_id`, `app0001_student_list`.`detail_id` FROM `app0001_student_list` WHERE `app0001_student_list`.`detail_id` = 1; args=(1,)
'张思'

models.Ban_list.objects.first().students.all()
(0.001) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list` ORDER BY `app0001_ban_list`.`id` ASC LIMIT 1; args=()
<QuerySet [<Student_list: 王小Python 18期2023-10-19>]>
(0.002) SELECT `app0001_student_list`.`id`, `app0001_student_list`.`sname`, `app0001_student_list`.`cid_id`, `app0001_student_list`.`detail_id` FROM `app0001_student_list` WHERE `app0001_student_list`.`cid_id` = 2 LIMIT 21; args=(2,)
(0.001) SELECT `app0001_student_list`.`id`, `app0001_student_list`.`sname`, `app0001_student_list`.`cid_id`, `app0001_student_list`.`detail_id` FROM `app0001_student_list` WHERE `app0001_student_list`.`cid_id` = 2 LIMIT 21; args=(2,)

.add() 的用法
class_obj = models.Ban_list.objects.first()
(0.002) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list` ORDER BY `app0001_ban_list`.`id` ASC LIMIT 1; args=()
(0.001) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list` LIMIT 21; args=()
(0.001) SELECT `app0001_teacher`.`id`, `app0001_teacher`.`tname` FROM `app0001_teacher` WHERE `app0001_teacher`.`tname` = 'Goldjin'; args=('Goldjin',)
(0.000) SELECT `app0001_teacher`.`id`, `app0001_teacher`.`tname` FROM `app0001_teacher` WHERE `app0001_teacher`.`tname` = 'Goldjin'; args=('Goldjin',)

models.Teacher.objects.get(tname='Goldjin').cid.add(class_obj)  直接添加对象
(0.001) SELECT `app0001_teacher`.`id`, `app0001_teacher`.`tname` FROM `app0001_teacher` WHERE `app0001_teacher`.`tname` = 'Goldjin'; args=('Goldjin',)
(0.001) SELECT `app0001_teacher_cid`.`ban_list_id` FROM `app0001_teacher_cid` WHERE (`app0001_teacher_cid`.`teacher_id` = 2 AND `app0001_teacher_cid`.`ban_list_id` IN (2)); args=(2, 2)
(0.057) INSERT INTO `app0001_teacher_cid` (`teacher_id`, `ban_list_id`) VALUES (2, 2); args=(2, 2)
(0.001) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list` LIMIT 21; args=()

多对多的取值是一样的 cid 对象 但是生成的第三张表中自动生成一个新的班级id ban_list_id
linux_45 = models.Ban_list.objects.last()
(0.002) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list` ORDER BY `app0001_ban_list`.`id` DESC LIMIT 1; args=()
(0.001) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list` LIMIT 21; args=()
(0.002) SELECT `app0001_teacher`.`id`, `app0001_teacher`.`tname` FROM `app0001_teacher` WHERE `app0001_teacher`.`tname` = 'alex'; args=('alex',)
(0.001) SELECT `app0001_teacher`.`id`, `app0001_teacher`.`tname` FROM `app0001_teacher` WHERE `app0001_teacher`.`tname` = 'alex'; args=('alex',)
models.Teacher.objects.get(tname='alex').cid.add(linux_45)
(0.001) SELECT `app0001_teacher`.`id`, `app0001_teacher`.`tname` FROM `app0001_teacher` WHERE `app0001_teacher`.`tname` = 'alex'; args=('alex',)
(0.001) SELECT `app0001_teacher_cid`.`ban_list_id` FROM `app0001_teacher_cid` WHERE id__in 是 个列表

(`app0001_teacher_cid`.`teacher_id` = 1 AND `app0001_teacher_cid`.`ban_list_id` IN (25)); args=(1, 25)

关于add(*list) 加上星的话 会打散 添加

class_list = models.Ban_list.objects.all()
(0.001) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list` LIMIT 21; args=()
(0.001) SELECT `app0001_teacher`.`id`, `app0001_teacher`.`tname` FROM `app0001_teacher` WHERE `app0001_teacher`.`tname` = 'alex'; args=('alex',)
(0.001) SELECT `app0001_teacher`.`id`, `app0001_teacher`.`tname` FROM `app0001_teacher` WHERE `app0001_teacher`.`tname` = 'alex'; args=('alex',)

models.Teacher.objects.get(tname='alex').cid.add(*class_list) 需要打散注意添加的是所有的课程 子安的课程
(0.001) SELECT `app0001_teacher`.`id`, `app0001_teacher`.`tname` FROM `app0001_teacher` WHERE `app0001_teacher`.`tname` = 'alex'; args=('alex',)
(0.002) SELECT `app0001_ban_list`.`id`, `app0001_ban_list`.`cname`, `app0001_ban_list`.`first_day` FROM `app0001_ban_list`; args=()
(0.002) SELECT `app0001_teacher_cid`.`ban_list_id` FROM `app0001_teacher_cid` WHERE (`app0001_teacher_cid`.`teacher_id` = 1 AND `app0001_teacher_cid`.`ban_list_id` IN (2, 7, 16, 18, 19, 20, 23, 24, 25)); args=(1, 2, 7, 16, 18, 19, 20, 23, 24, 25)
(0.001) INSERT INTO `app0001_teacher_cid` (`teacher_id`, `ban_list_id`) VALUES (1, 18), (1, 19), (1, 20), (1, 23), (1, 24); args=(1, 18, 1, 19, 1, 20, 1, 23, 1, 24)

1 remove() 或者clear 的方法操作后如果关联对象集为空的话 必须满足是null=True

day 69-70 一对一 一对多 多对一联表查询的相关教程结束。

《day 69-70 一对一 一对多 多对一联表查询.doc》

下载本文的Word格式文档,以方便收藏与打印。