第十二章 ORM的多表操作
1.多对多操作
1.1 环境准备
# test.py import os, django os.environ.setdefault(‘DJANGO_SETTINGS_MODULE‘, ‘项目名.settings‘) django.setup() from app01 import models
# models.py from django.db import models # 出版社和书的一对多关系 class Publisher(models.Model): name = models.CharField(max_length=32) def __str__(self): return ‘{}‘.format(self.name) # 书籍信息 class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=6, decimal_places=2) pub = models.ForeignKey(‘Publisher‘, on_delete=models.CASCADE) def __str__(self): return ‘{}‘.format(self.title) # 作者信息 class Author(models.Model): name = models.CharField(max_length=32) books = models.ManyToManyField(‘Book‘, related_name=‘authors‘, related_query_name=‘xxx‘) def __str__(self): return ‘{}‘.format(self.name)
1.2 基于对象的查询
正向查询
author = models.Author.objects.get(pk=1) # 关系管理对象,从1向多的方向拿都是关系管理对象 author.books # 所有书籍 models.Author.objects.get(pk=1).books.all()
反向查询(book表中没有外键字段)
多对多也同样适用
# book对象 book = models.Book.objects.filter(title=‘python之旅‘).first() # 关系管理对象,不指定related_name参数 book.author_set # 获取所有作者 book.author_set.all() # 指定related_name=‘authors‘ book.authors.all()
1.3 基于字段
# 不指定related_name/ related_query_name author = models.Author.objects.filter(books__title=‘python之旅‘) print(author) # 不指定related_name/ related_query_name book = models.Book.objects.filter(author__name=‘echo‘) print(book) # 指定related_name和related_query_name优先使用related_query_name=‘xxx‘ book = models.Book.objects.filter(xxx__name=‘echo‘) print(book)
1.4 关系管理对象方法(6)
通过关系管理对象获取多个关联值
# 关系管理对象 author = models.Author.objects.get(pk=1) book = models.Book.objects.get(pk=1) publisher = models.Publisher.objects.get(pk=1)
1.all()
books = author.books.all()
2. set([])
重新设置数据
值或对象
# 会覆盖历史数据 ret = author.books.set([‘书籍的id1‘,‘书籍的id2‘...]) # 返回值ret为None # 可以写对象 author.books.set(modles.Book.objects.filter(pk__in[1,2,3]))
3. add()
值或对象
# 添加数据,已有数据不会新增 author.books.add(‘书籍的id1‘,‘书籍的id2‘...) # 添加对象,* 表示打散 author.books.add(*models.Book.objects.filter(pk__in=[‘书籍的id1‘,‘书籍的id2‘...]))
4. remove()
值或对象
# 删除数据 author.books.remove(‘书籍的id1‘,‘书籍的id2‘...) # 删除对象 author.books.remove(*models.Book.objects.filter(pk__in=[‘书籍的id1‘,‘书籍的id2‘...]))
5.clear()
# 清除author对象的所有的多对多关系 author.books.clear()
6.create(字段=值)
# 创建书的信息,并添加关联信息 obj = author.books.create(title=‘python‘, pub_id=1) # 通过书创建作者 book = models.objects.get(pk=1) obj = book.authors.create(name=‘diane‘)
1.5 外键的方法
在外键中只能使用对象
pub和book关系
fitlter、get
1. all()
# 不指定related_name和related_query_name时,使用 类名_set获取关系管理对象 pub = models.Publisher.objects.get(pk=1) pub.book_set.all()
2. set(QuerySet对象)
对象列表
publisher = models.Publisher.objects.get(pk=1) # 不能使用id ,只能使用对象 publisher.books.set(models.Book.bojects.fitler(pk__in[4,5]))
3. add(*QuerySet)
一个个对象
publisher.books.add(*models.Book.bojects.fitler(pk__in[4,5]))
4. remove(*QuerySet)
一个个对象
# remove/clear, 外键必须设置成 null=True参数 publisher.books.remove(*models.Book.bojects.fitler(pk__in[4,5]))
5.clear()
publisher.books.clear()
6.create(字段=值)
models.Publisher.objects.get(pk=1).books.create(title=‘xxx‘, price=10)
Note
对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
2. 聚合和分组
aggregate()是QuerySet的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。
用到的内置的聚合函数
from django.db.models import Avg, Sum, Max, Min, Count, F, Q
2.1 聚合
aggregate(聚合函数)
ret = models.Book.objects.all().aggregate(Max(‘price‘)) # 省略all()也会生效 ret = models.Book.objects.aggregate(Max(‘price‘)) # dict 类型,可以 print(ret) # 如果给聚合结果重命名,注意位置和关键字传参的原则 ret = models.Book.objects.all().aggregate(Avg(‘price‘),max=Max(‘price‘))
# 返回值为字典,给终止子句 ret = models.Book.objects.filter(pk_gt=3).aggregate(Avg(‘price‘),max=Max(‘price‘))
2.2 分组
1. 基于字段
annotate注释,基于当前对象,添加一个注释字段
# .all()可以省略不写 ret = models.Book.objects[.all()].annotate(count=Count(‘author‘)) for i in ret: print(i.count)
2.分组方式1
# 以出版社的ID进行分组 ret = models.Publisher.objects.annotate(Min(‘book__price‘).values() for i in ret: print(i)
3. 分组方式2
values表示分组的字段
ret = models.Book.objects.values(‘pub/pub_id/pub__name‘).annotate(Min(‘price‘)) for i in ret: print(i) # 错误示范,如果values添加额外字段,则分组条件也会添加这个字段 ret = models.Book.objects.values(‘pub/pub_id/pub__name‘).annotate(Min(‘price‘)).values() # 正确 ret = models.Book.objects.values(‘pub/pub_id/pub__name‘).annotate(min=Min(‘price‘)).values(‘pub_id‘, min)
- 示例
# 统计每本书的作者个数 obj = models.Book.objects.values(‘title‘).annotate(Count(‘xxx__name‘)) for i in obj: print(i) # 统计出每个出版社买的最便宜的书的价格 obj = models.Publisher.objects.values(‘name‘).annotate(Min(‘xxx__price‘)) for i in obj: print(i) # 统计不止一个作者的图书,比较两种分组方式的区别 ret=models.Book.objects.annotate(count=Count(‘author__id‘)).filter(count__gt=1) print(ret) obj=models.Book.objects.values(‘title‘).annotate(count=Count(‘xxx__id‘)).filter(count__gt=1) print(obj) # 根据一本图书作者数量的多少对查询集 QuerySet进行排序 obj = models.Author.objects.annotate(count=Count(‘books__id‘)).order_by(‘count‘) print(obj) # 查询各个作者书的总价格 ret = models.Author.ojects.annotate(Sum(‘books__price‘)).values() print(ret)
3. F和Q查询
3.1 F查询
F(‘字段名‘),取出字段值进行相应操作
ret = models.Book.objects.filter(price__gt=100) print(ret)
1.比较两个字段值
# F,动态获取字段值
from django.db.models import F
ret = models.Book.objects.filter(sale__gt=F(‘inventory‘))
2.更新操作
# 更改一个对象,会更新所有的对象 obj = models.Book.objects.get(pk=1) obj.sale = 100 obj.save()
- 批量更新某一字段,update效率较高
# 批量更新,queryset对象支持update, 只更新sale字段 obj = models.Book.objects.filter(pk=1).update(sale=100) # 直接更新到数据库 models.Book.objects.filter(pk=1).update(sale=F(‘sale‘)*2+10)
3.2 Q查询
表示条件进行使用。
使用逻辑关系(| 或, & 与,~ 非)
from django.db.models import Q ret = models.Book.objects.filter(Q(pk__gt=3)|Q(pk__lt=2)) print(ret) # q条件的组合,逻辑判断 models.Book.objects.filter(~Q(Q(pk__gt=3)|Q(pk__lt=2))&Q(price__gt=50))
4. 事务
原子性、完整性
from django.db import transaction try: with transaction.atomic(): # orm操作为事务操作 models.Publiser.objects.create(name=‘xxx‘) int(‘sss‘) models.Publiser.objects.create(name=‘xxx2‘) except Exception as e: print(e)
5. Django终端打印SQL语句
Django项目的settings.py文件中,在最后复制粘贴如下代码
即为Django项目配置上一个名为django.db.backends的logger实例即可查看翻译后的SQL语句。
LOGGING = { ‘version‘: 1, ‘disable_existing_loggers‘: False, ‘handlers‘: { ‘console‘:{ ‘level‘:‘DEBUG‘, ‘class‘:‘logging.StreamHandler‘, }, }, ‘loggers‘: { ‘django.db.backends‘: { ‘handlers‘: [‘console‘], ‘propagate‘: True, ‘level‘:‘DEBUG‘, }, } }
6.执行原生sql
from django.db import connection cursor=connection.cursor() # 插入操作 cursor.execute("insert into hello_author(name) values(‘钱钟书‘)") # 更新操作 cursor.execute("update hello_author set name=‘abc‘ where name=‘bcd‘") # 删除操作 cursor.execute("delete from hello_author where name=‘abc‘") # 查询操作 cursor.execute("select * from hello_author") raw=cursor.fetchone() # 返回结果 cursor.fetchall() # 读取所有