Django ORM简介
ORM:Object对象,Relations关系、Mapping映射。简称:对象关系映射
优点:在Django中操作数据库就不用写原生的SQL语句,而是使用面向对象的语法和一些方法来操作数据库 缺点:代码的封装程度太高,导致的问题:执行效率下降了 ,目前可以忽略,需要自己写原生SQL语句
对象关系映射是通过面向对象的方式来操作数据库,这就需要对应的关系映射,数据中可以分为库,表,字段信息,一条条数据,而需要用面向对象的关系去对应。于是就有了下面对应关系。
-
数据库 ======= 面向对象模型
-
表 ======= 类
-
字段 ======= 类属性
-
记录 ======= 每个实例
么是ORM? 对象关系映射(Object Relational Mapping 简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术 ORM框架是连接数据库的桥梁 只要提供了持久化类与表的映射关系 ORM框架在运行时就能参照映射文件的信息 把对象持久化到数据库中 么用ORM? 当我们实现一个应用程序时不用ORM 我们可能会写特别多数据访问层的代码 从数据库保存、删除、读取对象信息 而这些代码都是重复的 而使用ORM则会大大减少重复性代码 对象关系映射主要实现程序对象到关系数据库数据的映射 类映射成表 对象映射成记录 对象点属性映射成字段对应的值
ORM的存在可以让不会MySQL的python程序员 使用python的语法简单快捷的操作MySQL先去应用目录下models.py中编写模型类 class User(models.Model): # 类似于定义了表名 id = models.AutoField(primary_key=True) # 类似于新建了记录ID主键 name = models.CharField(max_length=32) # 类似于定义了普通varchar字段 pwd = models.InteerField() # 类似于定义了
id int primary key auto_increment name varchar(32) pwd int'注意括号内可以添加多个属性''' 数据库迁移与同步命令 2.1>将models中有关数据库的操作记录下来到migrations文件夹中 python38 manage.py makemigrations
2.2>将操作命令真正的去操作数据库 python38 manage.py migrate改了models中与数据库相关的代码 都必须执行以上两个命令 要不然无效 :可以简写 也可以指定应用单独迁移/同步 Tools>Run Manage.py Task 表的主键在orm中 可以不写 orm会自动帮你添加一个id的主键 如果你需要主键的名称不叫id 只能自己创建
ORM事务操作
什么是事务?
事务是用户定义的一系列数据库操作,这些操作可以视为一个完整的逻辑处理工作单元。
要么全部执行,要么全部不执行,是不可分割的工作单元
事务的产生
数据库中的数据是共享资源,因此数据库系统通常要支持多个用户的或不同应用程序的访问,
并且各个访问进程都是独立执行的,这样就有可能出现并发存取数据的现象,为了避免数据库的不一致性
这种处理机制称之为"并发控制",其中事务就是为了保证数据的一致性而产生的的一种概念和手段(事务不是唯一手段)
事务的四大特征
为了保证数据库的正确性与一致性事务具有四个特征
-
原子性(Atomicity)- 事务的原子性保证事务中包含的一组更新操作是原子的,不可分割的。不可分割是事务最小的工作单位
- 所包含的操作被视为一个整体,执行过程中遵循着:要么全部执行,要么全部不执行,不存在一半执行一半未执行的情况
-
一致性(Consistency)- 事务的一致性要求事务必须满足数据库的完整性约束
- 且事务执行完毕后会将数据库由一个一致性的状态变为另一个一致性的状态,事务的一致性与原子性是密不可分的
-
隔离性(Isolation)- 事务的隔离性要求事务之间是彼此独立的,隔离的,及一个事务的执行不可以被其他事务干扰
-
持久性(Durability)- 事务的持续性也称为持久性,是指一个事务一旦提交,它对数据库的改变将是永久性的
- 因为数据刷进了物理磁盘了,其他操作将不会对它产生任何影响
相关SQL关键字:start transaction; rollback; commit; savapoint;
相关重要概念:脏读、幻读、不可重复读、MVCC多版本控制
ORM中如何使用事务
django,ORM中提供了至少三种开启事务的方式
方式一:全局开启事务
配置文件数据库(DATABASE)相关添加键值对 全局有效
"ATOMIC_REQUESTS":True每次请求所涉及到的ORM操作同属于一个事务
'如果过程中报错了,就会往回滚'
方式二:装饰器 局部有效
from django.db import transaction # 引入事务模块
@transaction.atomic # 原子装饰器
def index():pass
eg:
from django.db import transaction
@transaction.atomic
def index():
model.book.objects.create(title='简爱',price='345',publish_id=1)
hhshhsldlllds # 报错>>>就会过滚
return HttpResponse('添加成功')
return 123 #在事务里面,这也算成功
'''•
装饰器针对的是视图函数,当视图函数需要设计ORM操作,如果函数从上往下执行,
遇到报错会自动回滚到视图函数开始的状态。
'''
方式三:with 上下文管理 局部有效
from django.db import transaction
def reg():
with transaction.atomic():
pass # 这下面看作是同一个事务处理,遇到报错就会回滚
ORM字段类型
| 名称 | 含义 |
|---|---|
| AutoField() | Int自增列 必须填入参数 primary_key=True |
| 当model中如果没有自增列 则自动会创建一个列名为id的列 | |
| CharField() | 字符类型 必须提供max_length参数 max_length表示字符长度 |
| IntegerField() | 一个整数类型 范围在 -2147483648 to 2147483647 |
| (一般不用它来存手机号(位数也不够) 直接用字符串存) | |
| BigIntegerField() | 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 |
| DateField() | 日期字段 日期格式 YYYY-MM-DD 相当于Python中的datetime.date()实例 |
| DateTimeField() | 日期时间字段 格式 YYYY-MM-DD HH:MM:ss[.uuuuuu] |
| 相当于Python中datetime.datetime()实例 | |
| DecimalField() | 10进制小数 |
| 参数 max_digits 小数总长度 | |
| decimal_places,小数位长度 | |
| EmailField() | 字符串类型 Django Admin以及ModelForm中提供验证机制 |
| BooleanField() | 布尔值类型 传布尔值存数字0或1 |
| TextField() | 文本类型 存储大段文本 |
| FileField() | 字符串 路径保存在数据库 文件上传到指定目录 |
| 参数 upload_to = " " 上传文件的保存路径 | |
| storage = None 存储组件 默认django.core.files.storage.FileSystemStorage | |
| ForeignKey() | 外键类型在ORM中用来表示外键关联关系 一般把ForeignKey字段设置在 '一对多’中’多’的一方 |
| ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系 | |
| OneToOneField() | 一对一字段 通常一对一字段用来扩展已有字段 |
| 通俗的说就是一个人的所有信息不是放在一张表里面的,简单的信息一张表,隐私的信息另一张表,之间通过一对一外键关联 | |
| ManyToManyField() | 简单来说就是在多对多表关系并且这一张多对多的关系表是有Django自动帮你建的情况下 下面的方法才可使用create |
| add set remove clear |
需要说明的是,这些ORM字段并非与SQL字段一一对应,有些是封装了一些逻辑功能在字段的创建、存储过程中。
常用字段与不常用字段类型
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)
- 二进制类型
对应关系
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
ORM还支持用户自定义字段类型
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)
'回调之前的init方法'
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)
ORM字段参数
任意字段都可以设置的参数
| 名称 | 含义 |
|---|---|
| null | 用于表示某个字段可以为空。设置方式:null = True |
| unique | 用于表示该字段值在此表中必须是唯一的,建立唯一索引,设置方式:unique = True |
| db_index | 将该字段设置为索引,设置方式:db_index = True |
| default | 为该字段的默认值,设置方式:default = ‘默认值’ |
| db_column | 在数据库中的字段名称,默认和变量同名,设置方式:db_column=‘字段名’ |
| primary_key | 设置字段为主键,设置方式:primary_key = True。通常id字段为主键,且唯一主键 |
日期字段参数
| 名称 | 含义 |
|---|---|
| unique_for_date | 日期必须唯一 |
| unique_for_month | 月份必须唯一 |
| unique_for_year | 年份必须唯一 |
| auto_now_add | 增加记录时的时间 |
| auto_now | 更新当前记录的时间 |
用于Admin后台管理参数
| 名称 | 含义 |
|---|---|
| verbose_name | Admin中显示的字段名称 |
| blank | Admin中是否允许用户输入为空 |
| editable | Admin中是否可以编辑 |
| help_text | Admin中该字段的提示信息 |
| choices | Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作 |
| (设置方式:choice=((1,‘男’),(2,‘女’),(0,‘未知’)) ) |
choices 当某个字段的可能性能够被列举完全的情况下使用
eg:性别、学历、工作状态....
模型层数据:
class User(models.Model):
name = models.CharField(max_length=32)
info = models.CharField(max_length=32)
# 先写一个映射关系
gender_choices = (
(1,'男'),
(2,'女'),
(3,'其他'),
)
"""字段存储的范围还是取决于数据类型的字段"""
gender = models.SmallIntegerField(choices=gender_choices,null=True)
tests.py数据:
User_obj = models.User.objects.filter(pk=1).first() # 拿到一个对象
User_obj1 = models.User.objects.filter(pk=4).first() # 拿到一个对象
print(User_obj.gender) # 1 直接点现实存储的真实数据
'# 如果你存储的数据没有在映射范围内,就原样输出,如果有,就显示对应的关系对应的数据'
print(User_obj1.gender) # 4
'# get_字段名_display()'
print(User_obj.get_gender_display()) # 男 '通过这个方法拿显示转义后的选项'
print(User_obj1.get_gender_display()) # 4
其他参数
| 名称 | 含义 |
|---|---|
| max_length | 最大长度 |
| verboses_name | 别名 |
| max_digits | 数字中允许的最大的数字位数 |
| decimal_places | 数字的小数位数 |
| unique=True | 如果设置为unique=True 则该字段在此表中必须是唯一的 |
关系字段
表之间的关系由一对一、一对多、多对多来确定的
ForeignKey外键
通过外键来绑定各个表字段之间的关系
- to:关联哪张表、设置方式to = ‘关联表名’
- to_field:关联表的哪个字段(默认关联主键)
- related_name:反向查询时使用的字段名,用于代替原来反向查询的
表名_set - related_query_name:反向查询操作时 使用的连接前缀,用于代替表名。相当于给表设置了一个别名
on_delete参数设置的值
- models.CASCADE:删除关联数据,与之关联也删除
- models.DO_NOTHING:删除关联数据,引发错误IntegrityError
- models.PROTECT:删除关联数据,引发错误ProtectedError
- models.SET_NULL:删除关联数据,搭配null = True使用,使之关联的值为null
- models.SET_DEFAULT:删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
- models.SET:删除关联数据,设置为一个传递给SET()的值或者一个回调函数的返回值,注意大小写
db_constraint:保留跨表查询的方式,但是在table之间不建立外键关联(但是在Django内可以有一个关联状态,可以在admin里面看到),
一般都设置为 False,减少过多的约束。此时on_delete参数需要设置为models.SET_NULL、blank=True、null=True允许字段为空 ,这样即使关联的外键数据被删除了,也只会将字段设置为null。
OneToOneField与ForeignKey的区别
OneToOneField用于一对一关系的表之间进行关联。
根据Django官方文档介绍:
A one-to-one relationship. Conceptually, this is similar to a ForeignKey with unique=True, but the “reverse” side of the relation will directly return a single object.
OneToOneField与ForeignKey加上unique=True效果基本一样,但是用OneToOneField反向关联会直接返回对象。
相反地,使用ForeignKey, 反向关联后会返回QuerySet。
例如:Book表通过ForenignKey与Publish表建立了外键
obj = models.Publish.objects.filter(pk=1).first()
print(obj.book_set.all()) # 通过ForenignKey进行关联所以反向查询必须要加表名小写_set.all()
执行结果:拿到的是一个QuerySet对象
<QuerySet [<Book: Book object (1)>, <Book: Book object (2)>]>
Author表通过OneToOneField与AuthorDeatil表建立了外键
obj = models.AuthorDeatil.objects.filter(pk=1).first()
print(obj.author) # 通过OneToOneField进行关联的反向查询不需要_set.all()
执行结果:
Author object (1)
多对多关系建立的方式
两张表之间多对多的关系通常需要借助第三张表来存储,这样也更利于我们查询。而第三张表的建立有三种方式:
- 第一种:自动创建(常用:但是创建出来的第三张表没有其它字段,只存有这两张表的主键相互对应关系)
- 第二种:半手动创建(比较常用:第三张表我们可以建立其它字段,但也不影响两张表的关联)
- 第三种:完全手动建立第三张表。
第一种方式:通过ORM自带的ManyToManyField自动创建第三张表
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
books = models.ManyToManyField(to="Book", related_name="authors")
# 每次可以将Author表与Book表对应关系的主键值存储到第三张表
这种建立第三张表具备多个:多对多关系操作API
author_obj.books.add():括号内填写与作者关联的书籍对象 或者 书籍的主键值 author_obj.books.set():清空该作者原来关联的书籍,传入一个可迭代对象(里面可以包含一个或多个书籍对象 或者 书籍主键值) author_obj.books.clear():清空该作者关联的书籍 author_obj.books.remove():移除该作者关联的某个书籍
第二种方式:自己创建第三张表,并通过ManyToManyField指定关联
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book"))
# through_fields 元组的第一个值是ManyToManyField所在的表去中间表通过那个字段,就写在第一个位置。我们要通过author字段去中间表,因为author
class Author2Book(models.Model):
author = models.ForeignKey(to="Author")
book = models.ForeignKey(to="Book")
through:指定我们手动创建的中间表
through_fields:指定关联字段,设置方式通过元组,值也就是关联第三张表的哪几个字段,注意先后顺序:如果book写在前面则表示我们从Author表到中间表要通过book字段,那么就会乱套了
第三种方式:自己创建第三张表,分别通过外键关联书和作者
class Book1(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
class Author1(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
class Author2Book1(models.Model):
author = models.ForeignKey(to="Author")
book = models.ForeignKey(to="Book")
这种方式创建的多对多关系很多API操作都用不了,需要手动指定,如果要跨表查询将不能通过
obj.表名小写或者obj.表名小写_set.all(),而是每次都要通过中间表来实现。
ORM中执行SQL语句
在Django的ORM中我们也是可以使用原生SQL来进行操作的,其实我们并不能完全依赖于ORM框架来提供给我们操作数据库的便利,因为ORM在很多语言中使用形式都会有些不同,而SQL语句则是不变的。
实例:我们不需要管是基于什么对象来执行SQL语句,查出什么字段就可以直接使用
res = models.Author.objects.raw('select * from app01_book where id > 1')
for book in res:
print(book.name,book.price,book.publish_time)
执行结果:
Python爬虫从入门到入狱 666.5 2023-11-24
MySQL从入门到删库跑路 880.0 2023-10-24
总结:执行原生sql,跟对象类型无关了,查出什么字段,可以直接使用该字段。
ps:但是基于的不同对象执行SQL语句,得到的结果也会是不同的,有些字段数据还是用不了
Meta元信息
Meta是Django模型类里面的一个内部类,它用于定义一些Django模型类的行为特性。
定义方式:
class Author(models.Model):
class Meta:
pass
通常在里面定义变量来代表选项,而可用的选项大致包含以下几类: 1、db_table 自定义表名(通常表名都是由Django帮助我们创建的,且是由:项目_类名组合的)
2、index_together 为多个字段建立联合索引,设置方式:index_together = (‘name’,‘age’)
3、unique_together 当需要通过两个字段保持模型唯一性时使用,假设有一个 User 类,我们希望 User 的 username 和 email 两个属性的组合必须是唯一的,那么需要设置为:
unique_together = (("username", "email"),)
当某个一行记录:username='jack'、email='123@qq.com,那么其它行就不能同时这两个字段值都设置的和那一行一样。
unique_together 的值是一个元组的元组。为了方便起见,处理单一字段的集合时unique_together 可以是一维的元组
unique_together = ("driver", "restaurant")
这样就确保了driver与restaurant字段值是唯一的
4、ordering 根据哪个字段排序,设置方式:ordering = (‘id’,)
可配置参数
| 属性 | 描述 |
|---|---|
| db_table = ‘xxx’ | 修改表名为xxx |
| ordering = ‘xxx’ 按照指定字段xxx排序 | |
| verbose_name = ‘xxx’ | 给模型类指定一个直观可读的信息xxx |
| verbose_name_plural = verbose_name | 设置verbose_name的复数 |
| abstract = True | 设置模型类为一个基类(不会生成为表,只作为继承使用) |
| permissions = ((‘定义好的权限’, ‘权限说明’),) | 给数据库的表设置额外的权限 |
| managed = False | 是否按照django既定的规则来管理模型类 |
| unique_together = (‘address’, ‘note’) | 联合唯一键,约束 |
| app_label = ‘xxx’ | 定义模型类属于哪一个应用 |
| db_tablespace | 定义数据库表空间的名字 |
例子:
'关于Meta中'
例如:
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True,index=True)
username = models.CharField(max_length=32)
mobile=models.CharField(max_length=32)
class Meta:
# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
db_table = "table_name"
# 联合索引
index_together = [
("username", "mobile"),
]
# 联合唯一索引
unique_together = (("username", "mobile"),)
# admin中显示的表名称
verbose_name='图书表'
# verbose_name加s
verbose_name_plural
聚合查询
聚合查询是指对一个数据表中的一个字段的数据进行部分或者全部进行统计查询,例如计算平均值、总和、最大值、最小值、个数等。
聚合查询又分为整表聚合和分组聚合
整表聚合查询(aggregate)
整表聚合就是不带分组的聚合查询,即将全部数据进行集中统计查询
五种聚合函数
- Avg(Average):平均值
- Max(Maximum):最大值
- Min(Minimum):最小值
- Sum(Summary):求和
- Count:个数
当然还有更多的聚合函数可去查看官方文档查看docs.djangoproject.com/en/3.2/topi…
聚合查询需要先导入聚合函数
from django.db.models.import *
'常见的聚合函数有Sum、Avg、Count、Max、Min'
语法:
res = models.Book.objects.all().aggregate(结果变量名=聚合函数(‘列’))
返回结果:结果变量名和值组成的字典,即('结果变量名':值)
示例
from django.db.models import Avg,Max,Min,Sum,Count # 导入聚合函数
# 计算所有图书的平均值
res = models.Book.objects.all().aggregate(Avg('price'))
print(res)
结果为》》》:{'price__avg': 777.0}
aggregate(*args,**kwargs)
-
aggregate()是QuerySet对象的一个终止子句,意思是说,它返回一个包含一些键值对的字典 -
键的名称是聚合值的标识符,值是计算出来的聚合值
-
键的名称是按照字段和聚合函数的名称自动生成出来的
-
如果你想要为聚合值指定一个名称,可以向聚合子句提供它
es = models.Book.objects.all().aggregate(Avg_price=Avg('price')) # 指定名称 print(res) 结果为》》》:{'Avg_price': 777.0}
如果你希望生成的不止一个聚合,你可与向aggregate()子句中添加别的参数。
# 计算所有图书的总和、最大值、最小值、个数
res = models.Book.objects.all().aggregate(Min('price'),Count('price'),Sum_price=Sum('price'),Max_price=Max('price'))
print(res)
'''
和正常传参一样,有位置参数和关键字参数,要使用位置参数(不指定名称)必须放在关键字参数(指定名称)前面,
'''
结果为》》》:{'Sum_price': 3885, 'Max_price': 999, 'price__min': 555, 'price__count': 5}
分组聚合查询(annotate)
分组聚合就是指通过计算查询结果中的每一个对象所关联的对象集合,从而得出总计值(也可以平均值、总和等),即为查询集的每一项生成聚合
annotate()为调用的QuerySet每一个对象都生成一个独立的统计值(统计方法用聚合函数)
以字段为依据将相同的分为一类,annotate()分组关键字内写聚合函数 语法:
QuerySet.annotate(结果变量名=聚合函数('列'))
返回值为QuerySet
分组依据
-
values() 在 annotate() 之前则表示group by字段(分组),如果不写表示按整个表分组
默认分组依据' annotate()直接跟在object后面,则表示直接以当前的基表为分组依据 :按书来分组 models.Book.objects.all().annotate() 定分组依据' annotate()跟在values()后面,则表示按照values中指定的字段来进行分组 :按照书的价格进行分组 models.Book.objects.values('price').annotate()
- values() 在 annotate() 之后则表示取字段(只能取分组字段和聚合函数字段)
- filter() 在 annotate() 之前则表示where条件 (过滤)
- filter() 在 annotate() 之后则表示having条件
分组的目的:把有相同特征的分成一组,分成一组后一般用来,统计总条数、统计平均值、求最大值等。
示例
# 按书的价格进行分组
res = models.Book.objects.values('price').annotate()
# print(res)
# 查询出版社id大于1的出版社的id,以及书的平均价格
res = models.Publish.objects.values('id').filter(pk__gt=1).annotate(avg_price=Avg('book__price')).values('pk','avg_price')
# print(res)
# 查询出版社id大于1的出版社id,已经出书的平均价格大于666
res = models.Publish.objects.values('id').filter(pk__gt=1).annotate(avg_price=Avg('book__price')).filter(avg_price__gt=666).values('pk','avg_price')
print(res)
# 统计每一本书作者个数
res = models.Book.objects.all().annotate(count_authors=Count('authors__pk')).values('title','count_authors')
# print(res)
# 统计每一个出版社的最便宜的书
res = models.Publish.objects.all().annotate(min_book=Min('book__price')).values('name','min_book')
# # print(res)
# 统计不止一个作者的图书 (作者数量大于一)
res = models.Book.objects.annotate(count_authors=Count('authors')).filter(count_authors__gt=0).values('title','count_authors')
# print(res)
# 查询各个作者出的书的总价格
res = models.Author.objects.all().annotate(sum_price=Sum('book__price')).values('name','sum_price')
# print(res)
# 查询每个出版社的名称和书籍个数
res = models.Publish.objects.annotate(count_book=Count('book__id')).values('name','count_book')
# print(res)
# 查询每一个书籍的名称,以及对应的作者个数-->按书分
res = models.Book.objects.all().annotate(count_authors=Count('authors__pk')).values('title','count_authors')
# print(res)
# 查询每一个以 Python开头 书籍的名称,以及对应的作者个数-->按书分
res = models.Book.objects.all().filter(title__startswith='Python').annotate(count_authors=Count('authors__pk')).values('title','count_authors')
print(res)
F查询和Q查询
F查询
F查询:拿到某个字段在表中具体的值
从上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量作比较,如果我们要对两个字段的值作比较,那该如何做呢?
Django提供了F()来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个model实例中两个不同字段的值。
# 查询评论数大于收藏数的书籍
from django.db.models import F
Book.objects.filter(评论数__gt=F('收藏数'))
# 让所有图书价格+1
Book.objects.update(price=F("price")+1)
Q查询
Q查询:为了组装成 与 或 非 条件
与条件:and条件,在filter中直接写就是暗调条件
res = models.Book.objects.filter(title='MySQL从入门到删库跑路',price__gt=666)
print(res)
结果为》》》》:<QuerySet [<Book: MySQL从入门到删库跑路>]>
或条件:or条件
res = models.Book.objects.filter(Q(title='Python从入门到放弃')|Q(title='MySQL从入门到删库跑路'))
print(res)
结果为》》》》:<QuerySet [<Book: Python从入门到放弃>, <Book: MySQL从入门到删库跑路>]>
非条件:not条件
res = models.Book.objects.filter(~Q(title='论重启人生'))
print(res)
复杂逻辑:
res = models.Book.objects.filter((Q(title="论重启人生")& Q(price__gt=666))| Q (id__gt=2))
字段参数
ForeignKey属性
- related_name 反向操作时,使用的字段名,用于代替原反向查询时的’表名小写_set’
- related_query_name 反向查询操作时,使用的连接前缀,用于替代表名
ManyToManyField 用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系
- to 设置要关联的表,中间是有个中间表的,区别于一对多
- related_name 同ForeignKey字段。
- related_query_name 同ForeignKey字段。
- through 在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。 但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。
- through_fields设置关联的字段。
- db_table 默认创建第三张表时,数据库中表的名称。
参考文章 https: // blog.csdn.net / achen_m / article / details / 135018206