一、多表关系
回顾一下设置外键的sql语句:
foreign key(外键字段) references 关联表(关联字段)
设置级联关系:
on update cascade # 更新级联关系,要改一起改 on delete cascade # 删除历练关系,要死一起死
- 一对一
外键在任何一方都可以,但是尽量放在使用的次数多的一方,并设置字段唯一键,
- 一对多
外键放在多的一方,此时外键不唯一
- 多对多
一定要创建第三张表(关系表),每一个外键值不唯一,但可以多个外键建立联合唯一
二、多表操作
首先多表操作,应该在建表之前,就先设计好多表关系,然后再去创建表。
2.1 分析多表关系
需求:现在需要写一个图书管理的程序,需要设计表。如何设计
图书表
图书表包括(书名、价格、出版日期)
出版社表
出版社表包括(出版社名、地址)
作者简介表
作者简介表包括(作者名、年龄)
作者详情表
作者详情表包括(电话、地址)
- 一对一:一个作者简介唯一对应作者详情表,一个作者详情也唯一对应作者简介
- 一对多:一个图书对应唯一对应一个出版社,一个出版社可以对应多本书
- 多对多:一个图书对应多个作者,一个作者也同样对应多本书
2.2 创建表
还是在模型层中去创建表
class Book(models.Model): name = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) # 长度8位,两位小数 publish_date = models.DateField(auto_now_add=True) '''一对多关系 ForeignKey''' publish = models.ForeignKey(to="Publish") # 不能加id '''多对多关系 ManyToManyField''' author = models.ManyToManyField(to="Author") # 只需要取要关联的第三表,不能加id class Publish(models.Model): name = models.CharField(max_length=32) addr = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() author_detail = models.OneToOneField("AuthorDetail") class AuthorDetail(models.Model): Phone = models.BigIntegerField() addr = models.CharField(max_length=32)
2.3 外键字段的增删改查
增,先增加被关联表数据
# 出版社表 models.Publish.objects.create(name="东方出版",addr="北京") models.Publish.objects.create(name="南方出版",addr="南京") # 作者详情表 models.AuthorDetail.objects.create(Phone="110", addr="上海") models.AuthorDetail.objects.create(Phone="120", addr="安徽") models.AuthorDetail.objects.create(Phone="119", addr="重庆") # 作者简介表 models.Author.objects.create(name="haha",age=18,author_detail_id=1) models.Author.objects.create(name="aaa",age=19,author_detail_id=2) models.Author.objects.create(name="bbb",age=29,author_detail_id=3) # 书表 models.Book.objects.create(name="python从入门到放弃",price=12,publish_id=1) models.Book.objects.create(name="鬼谷子",price=123,publish_id=2) models.Book.objects.create(name="墨菲定律",price=12,publish_id=1)
查
book_obj = models.Book.objects.filter(pk=2).first() print(book_obj.publish) # 获取到当前所对应的出版社对象 print(book_obj.publish_id) # 获取到的就是表中的实际字段
改 默认是级联更新,级联删除
models.Book.objects.filter(pk=2).update(publish_id=1) # _id 直接给出实际id publish_obj = models.Publish.objects.filter(pk=2).first() models.Book.objects.filter(pk=2).update(publish=publish_obj) # publish_obj做更新,默认就是id
删除 默认是级联更新,级联删除
models.Publish.objects.filter(pk=2).delete()
注意: 如果要用外键字段
直接是用表类中外键字段名就要传一个对象,如果是数据库中的外键字段名,就传id号
2.4 多对多字段的四个方法
- 增 add()
""" add() 括号内既可以传数字也可以传数据对象 并且都支持传多个 """ # 通过id book_obj = models.Book.objects.filter(pk=3).first() print(book_obj.author) # 就相当于 已经在书籍和作者的关系表了 book_obj.author.add(2,6) # 从书籍表中找到第三张关系表中 添加作者id 2和6 # 通过对象 book_obj = models.Book.objects.filter(pk=2).first() author_obj = models.Author.objects.filter(pk=1).first() author_obj1 = models.Author.objects.filter(pk=2).first() book_obj.author.add(author_obj,author_obj1) # 通过书籍表中找到第三张关系表 添加author_obj的主键
- 改 set()
""" set() 括号内 既可以传数字也传对象 并且也是支持传多个的 但是需要注意 括号内必须是一个可迭代对象 """ # 通过id book_obj = models.Book.objects.filter(pk=3).first() # 将作者主键为3的纪录全部重设成set中的 book_obj.author.set([1,2,6]) # 通过对象 book_obj = models.Book.objects.filter(pk=2).first() # 将作者主键为2的纪录全部重设set中的 author_obj = models.Author.objects.filter(pk=1).first() author_obj1 = models.Author.objects.filter(pk=2).first() book_obj.author.set((author_obj,author_obj1))
- 删 remove()
""" remove() 括号内 既可以传数字也传对象 并且也是支持传多个的 """ # 通过id book_obj = models.Book.objects.filter(pk=3).first() # 将作者主键为3的纪录并且,作者id满足删除 book_obj.author.remove(1,2) # 通过对象 author_obj = models.Author.objects.filter(pk=1).first() author_obj1 = models.Author.objects.filter(pk=2).first() book_obj = models.Book.objects.filter(pk=2).first() # 将作者主键为3的纪录并且,作者id满足删除 book_obj.author.remove(author_obj,author_obj1)
- 清空 clear()
"""clear()括号内不需要传任何参数 直接清空当前书籍对象所有的记录""" book_obj = models.Book.objects.filter(pk=3).first() book_obj.author.clear()
三、跨表查询
ORM跨表查询:
- 子查询
- 连表查询
正反向的概念 :
- 外键字段在谁那儿 由谁查谁就是正向
- 谁手里有外键字段 谁就是正向查,没有外键字段的就是反向
跨表查询最重要的口诀:
- 正向查询按字段
- 反向查询按表名小写...
3.1 子查询
基于对象的跨表查询
'''正向查询按字段''' ''' 什么时候需要.all() 当查询的结果可以是多个的情况下 需要加.all() ''' # 1.查询书籍是 python从入门到放弃 的出版社名称 book_obj = models.Book.objects.filter(name="python从入门到放弃").first() print(book_obj.publish.name) # 2.查询书籍主键是3的作者姓名 book_obj = models.Book.objects.filter(pk=3).first() print(book_obj.author) # app01.Author.None代表有多条记录要使用all() print(book_obj.author.all()) # 3.查询作者是 hhh 的手机号 author_obj = models.Author.objects.filter(name="hhh").first() print(author_obj.author_detail.Phone) '''反向查询按表名小写 ''' ''' 只要有多个关系就要加_set 当查询的结果可以是多个的情况下 需要加_set.all() 什么时候不需要加_set 当查询的结果有且只有一个的情况下 不需要加任何东西 直接表名小写即可 ''' # 4.查询出版社是 东方出版 出版过的书籍 publish_obj = models.Publish.objects.filter(name="东方出版").first() print(publish_obj.book_set.all()) # 查询的是多个就要加_set,不然会报错。app01.Book.None,要加all() # 5.查询作者是 hhh 写过的所有的书 author_obj = models.Author.objects.filter(name='hhh').first() print(author_obj.book_set.all()) # 6.查询手机号是110的作者 authorDetail_obj = models.AuthorDetail.objects.filter(Phone=110).first() print(authorDetail_obj.author.name) # 7.查询书籍是 python从入门到放弃 的作者的手机号 author_dic = models.Book.objects.filter(name="python从入门到放弃").values("author").first() author_id = author_dic.get("author") author_obj = models.Author.objects.filter(pk=author_id).first() print(author_obj.author_detail.Phone)
3.2 连表查询
基于双下划线的跨表查询
优点:它可以无限跨表,只要有关系
''' MySQL left join inner join right join union ORM多表查询口诀: 正向查询按字段 反向查询按表名小写 ''' # 1.查询书籍是 python从入门到放弃 的出版社名称 # 正向 res = models.Book.objects.filter(name="python从入门到放弃").values("publish__name") print(res) # 反向 res = models.Publish.objects.filter(book__name="python从入门到放弃").values("name") print(res) # 2.查询作者是 hhh 的手机号码 # 正向 res = models.Author.objects.filter(name='hhh').values('author_detail__Phone') print(res) # 反向 res = models.AuthorDetail.objects.filter(author__name="hhh").values("Phone") print(res) # 3.查询手机号是120的作者姓名 # 反向: res = models.AuthorDetail.objects.filter(Phone=120).values("author__name") print(res) # 正向: res = models.Author.objects.filter(author_detail__Phone=120).values("name") print(res) # 先连表,然后拿name字段 # 4.查询出版社是 东方出版 出版的书籍名称 # 反向 res = models.Publish.objects.filter(name="东方出版").values("book__name") print(res) # 正向 res = models.Book.objects.filter(publish__name="东方出版").values("name") print(res) # 5.查询作者是 hhh 的写过的书的名字和价格 # 正向 res = models.Author.objects.filter(name="hhh").values("book__name","book__price") print(res) # 反向 res = models.Book.objects.filter(author__name="hhh").values("name","price") print(res) # 查询书籍是 python从入门到放弃 的作者的手机号 # 正向 res = models.Book.objects.filter(name="python从入门到放弃").values("author__author_detail__Phone") print(res) # 反向 res = models.AuthorDetail.objects.filter(author__book__name="python从入门到放弃").values("Phone") print(res)