第十二章 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() # 读取所有