What is ORM Django ?
ORM概念
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
ORM在业务逻辑层和数据库层之间充当了桥梁的作用。
ORM由来
让我们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。
几乎所有的软件开发过程中都会涉及到对象和关系数据库。在用户层面和业务逻辑层面,我们是面向对象的。当对象的信息发生变化的时候,我们就需要把对象的信息保存在关系数据库中。
按照之前的方式来进行开发就会出现程序员会在自己的业务逻辑代码中夹杂很多SQL语句用来增加、读取、修改、删除相关数据,而这些代码通常都是重复的。
ORM的优势
ORM解决的主要问题是对象和关系的映射。它通常把一个类和一个表一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段。
ORM提供了对数据库的映射,不用直接编写SQL代码,只需像操作对象一样从数据库操作数据。
让软件开发人员专注于业务逻辑的处理,提高了开发效率。
ORM的劣势
ORM的缺点是会在一定程度上牺牲程序的执行效率。
ORM用多了SQL语句就不会写了,关系数据库相关技能退化...
ORM总结
ORM只是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。
但我们不能指望某个工具能一劳永逸地解决所有问题,一些特殊问题还是需要特殊处理的。
但是在整个软件开发过程中需要特殊处理的情况应该都是很少的,否则所谓的工具也就失去了它存在的意义。
django 中使用 ORM
在Django项目的settings.py文件中,配置数据库连接信息
# 链接数据库mysql
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "你的数据库名称", # 需要自己手动创建数据库
"USER": "数据库用户名",
"PASSWORD": "数据库密码",
"HOST": "数据库IP",
"POST": 3306
}
}
- 特别注意的是用mysql init 文件要添加这句话
import pymysql
pymysql.install_as_MySQLdb()
Model 模型
在Django中model是你数据的单一、明确的信息来源。它包含了你存储的数据的重要字段和行为。通常,一个模型(model)映射到一个数据库表.
基本情况
- 每个模型都是一个Python类,它是django.db.models.Model的子类。
- 模型的每个属性都代表一个数据库字段。
- 综上所述,Django为您提供了一个自动生成的数据库访问API
快速入门
下面这个例子定义了一个 Person 模型,包含 first_name 和 last_name。
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
first_name 和 last_name 是模型的字段。每个字段被指定为一个类属性,每个属性映射到一个数据库列。
sql
CREATE TABLE myapp_person (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
- 表myapp_person的名称是自动生成的,如果你要自定义表名,需要在model的Meta类中指定 db_table 参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时
- id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。
- 本示例中的CREATE TABLE SQL使用PostgreSQL语法进行格式化,但值得注意的是,Django会根据配置文件中指定的数据库后端类型来生成相应的SQL语句。
- Django支持MySQL5.5及更高版本
AutoField:自增字段
- int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。
IntegerField:整数字段
- 一个整数类型,范围在 -2147483648 to 2147483647。
CharField:字符字段
- 字符类型,必须提供max_length参数, max_length表示字符长度。
DateField:日期字段
- 日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。
DateTimeField:日期时间字段
- 日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。
Foreign key:外键字段,一般用于一对多或则多对一,同时外键一般健在多的那一边。
ManyToManyField:用于关联两张表,一般是多对多,将两张表关联起来
==那么多对多的创建我们又有三种方式:ManyToManyField==
- 使用ManyToManyField() --> ORM自动帮我创建第三张表,这种方式借助ORM帮我们创建一个第三张表。
- 手动创建第三张表:我们直接在数据库自己手动创建第三张表。
- 手动创建第三张表并设置ManyToManyField(),指定
books = models.ManyToManyField(
to="Book",
through="Book2Author",
through_fields=('author', 'book')
)
4 所以字段合集
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
5.自定义字段
class FixedCharField(models.Field):
"""
自定义的char类型的字段类
"""
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super(FixedCharField, self).__init__(max_length=max_length, *args, **kwargs)
def db_type(self, connection):
"""
限定生成数据库表的字段类型为char,长度为max_length指定的值
"""
return 'char(%s)' % self.max_length
class Class(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=25)
# 使用自定义的char类型的字段
cname = FixedCharField(max_length=25)
6. 字段参数
null
用于表示某个字段可以为空。
unique
如果设置为unique=True 则该字段在此表中必须是唯一的 。
db_index
如果db_index=True 则代表着为此字段设置索引。
default
为该字段设置默认值。
7. DateField和DateTimeField
auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段。
8 . Foreign key OnetoOneFeild ManytoManyField
关系字段
ForeignKey
外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。
ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。
字段参数
to
设置要关联的表
to_field
设置要关联的表的字段
related_name
反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。
例如:
class Classes(models.Model):
name = models.CharField(max_length=32)
class Student(models.Model):
name = models.CharField(max_length=32)
theclass = models.ForeignKey(to="Classes")
当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:
models.Classes.objects.first().student_set.all()
当我们在ForeignKey字段中添加了参数 related_name 后,
class Student(models.Model):
name = models.CharField(max_length=32)
theclass = models.ForeignKey(to="Classes", related_name="students")
当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:
models.Classes.objects.first().students.all()
related_query_name
反向查询操作时,使用的连接前缀,用于替换表名。
on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。
models.CASCADE
删除关联数据,与之关联也删除
models.DO_NOTHING
删除关联数据,引发错误IntegrityError
models.PROTECT
删除关联数据,引发错误ProtectedError
models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
models.SET
删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
def func():
return 10
class MyModel(models.Model):
user = models.ForeignKey(
to="User",
to_field="id",
on_delete=models.SET(func)
)
db_constraint
是否在数据库中创建外键约束,默认为True。
OneToOneField
一对一字段。
通常一对一字段用来扩展已有字段。
字段参数
to
设置要关联的表。
to_field
设置要关联的字段。
on_delete
同ForeignKey字段。
ManyToManyField
用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系。
字段参数
to
设置要关联的表
related_name
同ForeignKey字段。
related_query_name
同ForeignKey字段。
symmetrical
仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True。
举个例子:
class Person(models.Model):
name = models.CharField(max_length=16)
friends = models.ManyToManyField("self")
此时,person对象就没有person_set属性。
class Person(models.Model):
name = models.CharField(max_length=16)
friends = models.ManyToManyField("self", symmetrical=False)
此时,person对象现在就可以使用person_set属性进行反向查询。
through
在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。
但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。
through_fields
设置关联的字段。
db_table
默认创建第三张表时,数据库中表的名称。
8.元信息
db_table
ORM在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名。
index_together
联合索引。
unique_together
联合唯一索引。
ordering
- 指定默认按什么字段排序。只有设置了该属性,我们查询到的结果才可以被reverse()
2.关于Django中的ORM操作必知必会十三条
<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(): 对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。
<9> distinct(): 从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)
<10> count(): 返回数据库中匹配查询(QuerySet)的对象数量。
<11> first(): 返回第一条记录
<12> last(): 返回最后一条记录
<13> exists(): 如果QuerySet包含数据,就返回True,否则返回False
3. Django ORM query_demo
#ret = models.Book.objects.all() # QuerySet类型 --> 书籍对象的列表
print(ret)
# ret = models.Book.objects.filter(title="围城") # QuerySet类型 --> 书籍对象的列表
# print(ret)
# ret = models.Book.objects.filter(id=1) # QuerySet类型 --> 书籍对象的列表
# print(ret)
# id值大于1
# ret = models.Book.objects.filter(id__gt=1) # QuerySet类型 --> 书籍对象的列表
# print(ret)
# id值小于3
# ret = models.Book.objects.filter(id__lt=3) # QuerySet类型 --> 书籍对象的列表
# print(ret)
# 出版日期是2017年的书
# ret = models.Book.objects.filter(publisher_date__year=2017) # QuerySet类型 --> 书籍对象的列表
# print(ret)
# 出版日期大于2017年
# ret = models.Book.objects.filter(publisher_date__year__gt=2017) # QuerySet类型 --> 书籍对象的列表
# print(ret)
# 书名中包含'曌'的书
# ret = models.Book.objects.filter(title__contains="曌") # QuerySet类型 --> 书籍对象的列表
# print(ret)
# 书名中包含'曌'的书并且出版年份是2018年
# ret = models.Book.objects.filter(title__contains="曌", publisher_date__year=2018) # QuerySet类型 --> 书籍对象的列表
# print(ret)
# get方法 如果检索条件找不到对象就报错
# ret = models.Book.objects.get(id=10) # 具体的类对象实例 --> 书籍对象
# print(ret)
# 使用filter检索的时候没有满足条件的数据就返回一个空 QuerySet
# ret = models.Book.objects.filter(id=10) # 具体的类对象实例 --> 书籍对象
# print(ret)
# 将满足条件的去掉,留下不满足条件的
# ret = models.Book.objects.exclude(id__in=[1,3,4])
# print(ret)
# values 取字段的值
# ret = models.Book.objects.filter(publisher_date__year=2018).values("title", "publisher_date")
# QuerySet类型 --> 字段及字段值的字典的列表
# <QuerySet [{'id': 2, 'title': '牧羊少年奇幻漂流', 'price': 50}, {'id': 3, 'title': '一个人的朝圣', 'price': 40}]>
# print(ret)
#
# ret = models.Book.objects.filter(publisher_date__year=2018).values_list("title", "publisher_date")
# QuerySet类型 --> 字段值的元祖的列表
#结果为:<QuerySet [(2, '牧羊少年奇幻漂流', 50), (3, '一个人的朝圣', 40)]>
# print(ret)
# 按字段排序
# ret = models.Book.objects.all().order_by("price") # QuerySet类型 --> 根据price字段对所有数据排序
# print(ret)
#
# ret = models.Book.objects.all().order_by("-price") # QuerySet类型 --> 根据price字段对所有数据排序
# print(ret)
#
# ret = models.Book.objects.all().order_by("price").reverse() # QuerySet类型 --> 根据price字段对所有数据排序
# print(ret)
#
# # 按照出版时间排序
# ret = models.Book.objects.all().order_by("publisher_date").reverse().values("title") # QuerySet类型 --> 根据price字段对所有数据排序
# print(ret)
# 连表查询
# ret = models.Book.objects.all().values("publisher__name").distinct() # QuerySet类型 --> 根据price字段对所有数据排序
# print(ret)
# count 计数
# ret = models.Book.objects.all().count() # 数字 --> 结果集中数据的个数
# print(ret)
# first()和last()
# ret = models.Book.objects.all().first() # 对象 --> 结果集中的第一个数据
# print(ret)
# 判断结果集中是否有数据
# ret = models.Book.objects.all().exists() # 布尔值 --> 结果集中是否有数据
# print(ret)
4.按照返回值分类
返回QuerySet对象的方法有
all()
filter()
exclude()
order_by()
reverse()
distinct()
特殊的QuerySet
values() 返回一个可迭代的字典序列
values_list() 返回一个可迭代的元祖序列
返回具体对象的
get()
first()
last()
返回布尔值的方法有:
exists()
返回数字的方法有
count()
5.单表查询中双下划线
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)
6.ForeignKey 相关操作
1.正向查找
正向查找:那么什么是正向查找,我们知道对于一对多或者多对一的情况,我们一般将ForeignKey设置在多的一边,比如我们的书籍与出版社一般是多对一的,所以当书籍去找出版社的时候就是正向查找.
正向查找分为: 对象查找 字段查找
1.对象查找
#语法:对象.关联字段.字段 ----->对象加关联字段再加上字段
book_obj = models.Book.objects.first() # 第一本书对象,先拿到对象。
print(book_obj.publisher) # 得到这本书关联的出版社对象,拿到关联的字段,也就是publisher。
print(book_obj.publisher.name) # 得到出版社对象的名称,其实拿到关联字段之后就相当于拿到了publisher这个对象,然后再通过name字段取我们的publisher中的出版社name。
2.字段查找(常用)
# 关联字段__字段 通过关联字段其实拿到我们要查询的表,然后通过双下划綫“__”加上要查询表的字段name。
print(models.Book.objects.values_list("publisher__name")) #这里双下划线其实类似于一个桥梁,连接起来两个表。
2.反向查找
反向查找:是与正向查找相反的,也就是说他是基于出版社来查找我们的书籍表。
1.对象查找
# obj.表名_set --> 先拿到我们当前表的第一个对象,然后通过对象表名加“__”双下划綫的形式,找到对应的所有记录。
publisher_obj = models.Publisher.objects.first() # 找到第一个出版社对象
books = publisher_obj.book_set.all() # 找到第一个出版社出版的所有书
titles = books.values_list("title") # 找到第一个出版社出版的所有书的书名
2.字段查找
# 表名__字段 --> 直接对象表名加“__”加上需要查询的字段的名字。
titles = models.Publisher.objects.values_list("book__title")
7.ManyToManyField
关联管理器"是在一对多或者多对多的关联上下文中使用的管理器
存在于下面两种情:
- 外键关系的反向查询
- 多对多关联关系
简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法
方法
create()
# 创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象
import datetime
author_obj = models.Author.objects.first().book_set.create(title="番茄物语", publish_date=datetime.date.today())
# book_set 后面的 create 就是创建了一个 book对象实例
add()
# 把指定的model对象添加到关联对象集中
author_objs = models.Author.objects.filter(id__lt=3) # 多个对象
models.Book.objects.first().authors.add(*author_objs)
# 添加ID list
models.Book.objects.first().authors.add(*[1, 2])
set()
更新model对象的关联对象 删除所有的关系 添加新的关系
book_obj = models.Book.objects.first()
book_obj.authors.set([2, 3])
remove()
# 从关联对象集中移除执行的model对象
book_obj = models.Book.objects.first()
# 把 book_obj的author 所有对象中 移除id = 3 的author对象
book_obj.authors.remove(3)
clear()
# 从关联对象集中移除一切对象
book_obj = models.Book.objects.first()
book_obj.authors.clear() # 移除全部authors 对象
注意:
对于ForeignKey对象,clear()和remove()方法仅在null=True时存在
example
# 没有设置null = True 的时候
class Book(models.Model):
title = models.CharField(max_length=32)
publisher = models.ForeignKey(to=Publisher)
# 没有clear()和remove()方法
>>> models.Publisher.objects.first().book_set.clear()
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'RelatedManager' object has no attribute 'clear'
# 当ForeignKey字段设置null=True时
class Book(models.Model):
name = models.CharField(max_length=32)
publisher = models.ForeignKey(to=Class, null=True)
# 此时就有clear()和remove()方法
>>> models.Publisher.objects.first().book_set.clear()
## OK
注意
对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法
8.聚合查询和分组查询
聚合:使用聚合函数对我们查询的结果进行进一步操作
简单的使用
- 聚合其实类似于我们的SQL语句,其中在链表查询的时候也用到了聚合函数。
- aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
- 键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。
- 用到的内置函数
from django.db.models import Avg, Sum, Max, Min, Count
example
from django.db.models import Avg, Sum, Max, Min, Count
models.Book.objects.all().aggregate(Avg("price"))
{'price__avg': 13.233333}
如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
models.Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 13.233333}
# 如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))
{'price__avg': 13.233333, 'price__max': Decimal('19.90'), 'price__min': Decimal('9.90')}
9.分组查询
# 统计每一本书的作者个数
book_list = models.Book.objects.all().annotate(author_num=Count("author"))
for obj in book_list:
print(obj.author_num) # 打印每一本书的作者个数
# 统计出每个出版社买的最便宜的书的价格
1.
publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
for obj in publisher_list:
print(obj.min_price)
2.
models.Book.objects.values("publisher__name").annotate(min_price=Min("price"))
<QuerySet [{'publisher__name': '沙河出版社', 'min_price': Decimal('9.90')}, {'publisher__name': '人民出版社', 'min_price': Decimal('19.90')}]>
# 统计不止一个作者的图书
models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)
<QuerySet [<Book: 番茄物语>]>
# 根据一本图书作者数量的多少对查询集 QuerySet进行排序
models.Book.objects.annotate(author_num=Count("author")).order_by("author_num")
<QuerySet [<Book: 香蕉物语>, <Book: 橘子物语>, <Book: 番茄物语>]>
# 查询各个作者出的书的总价格
models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")
<QuerySet [{'name': '小精灵', 'sum_price': Decimal('9.90')}, {'name': '小仙女', 'sum_price': Decimal('29.80')},
{'name': '小魔女', 'sum_price': Decimal('9.90')}]>
原文地址:https://www.cnblogs.com/zjcode/p/9348804.html