【Django】第06回 聚合、分组、F、Q 查询、ORM查询优化、常见字段

1. 正反向查询进阶操作

# 1.查询主键为1的书籍对应的出版社名称及书名
res = models.Publish.objects.filter(book__pk=1).values('name','book__title')
print(res)

# 2.查询主键为3的书籍对应的作者姓名及书名
res = models.Author.objects.filter(book__pk=3).values('name', 'book__title')
print(res)

# 3.查询jason的作者的电话号码和地址
res = models.AuthorDetail.objects.filter(author__name='jason').values('phone', 'addr')
 print(res)

# 4.查询南方出版社出版的书籍名称和价格
res = models.Book.objects.filter(publish__name='南方出版社').values('title','price')
print(res)

 # 5.查询jason写过的书的名称和日期
res = models.Book.objects.filter(authors__name='jason').values('title', )
print(res)

# 6.查询电话是110的作者姓名和年龄
res = models.Author.objects.filter(author_detail__phone=110).values('name', 'age')
print(res)

#7.查询主键为1的书籍对应的作者电话号码
res = models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')
print(res)
res1 = models.Author.objects.filter(book__pk=1).values('author_detail__phone')
print(res)

2. 聚合查询

聚合函数:max、min、sum、avg、count
导入模块:django.db.models(跟数据查询相关的模块,方法一般都是django.db.models)
aggregate: 没有分组之前如果单纯的时候聚合函,需要关键字aggregate
from django.db.models import Max, Min, Sum, Avg, Count
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
print(res)

3. 分组查询

分组特性

分组有一个特性 默认只能够直接获取分组的字段 其他字段需要使用方法
我们也可以忽略掉该特性 将sql_mode中only_full_group_by配置移除即可

分组方式

1. 按照整条数据分组
   models.Book.objects.annotate() 按照一条条书籍分组
2. 按照表中某个字段分组
   models.Book.objects.values('title').annotate() 按照annotate之前values括号中指定的字段分组

示例

# 示例1:统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
print(res)
res1 = models.Book.objects.values('publish_id').annotate(book_num=Count('pk')).values('publish_id','book_num')
print(res1)

# 示例2:统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).value('name','min_price')
print(res)

# 示例3:统计不止一个作者的图书
'''filter在annotate前面则是where 在annotate后面则是having'''
res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title', 'author_num')
print(res)

# 示例4:查询各个作者出的书的总价格
res = models.Author.objects.annotate(book_sum_price=Sum('book__price')).values('name','book_sum_price')
print(res)

4.F与Q查询

4.1 给表添加新的字段

当表中已经有数据的情况下,添加新的字段需要指定一些参数
  1. 设置字段值允许为空
  2. 设置字段默认值
  3. 在终端中直接给出默认值

4.2 F查询

1. Django提供F()来做两个字段值比较,F()的实例可以在查询中引用字段,来比较同一个model实例中两个不同字段的值.
   F查询:查询条件不是自定义的而是来自于表中其他字段
   from django.db.models import F

示例:查询库存数大于卖出数的书籍
res = models.Book.objects.filter(storage_num__gt=F('sale_num'))
print(res)


2. Django支持F()对象之间以及F()对象和常数之间的加减乘除和取模的操作。基于此可以对表中的数值类型进行数学运算

示例:将所有书籍的价格上涨1000块
models.Book.objects.update(price=F('price') + 1000)


3. 如果修改char字段,不能用对数值类型的操作,拼接:Concal表示进行字符串的拼接操作,参数位置决定了拼接是在头部拼接还是尾部拼接,Value里面是要新增的拼接值

示例:将所有书籍名称加上爆款后缀
# models.Book.objects.filter(pk=5).update(title=F('title') + '爆款')  # 针对字符串数据无法直接拼接
from django.db.models.functions import Concat
from django.db.models import Value
ret3 = models.Book.objects.filter(pk=5).update(title=Concat(F('title'), Value('爆款')))

4.3 Q查询

Q查询:可以改变filter括号内多个条件之间的逻辑运算符,还可以将查询条件的字段改为字符串形式

(1)可以改变filter括号内多个条件之间的逻辑运算符
1. | 管道符 or关系
from django.db.models import Q
# res = models.Book.objects.filter(pk=1, publish_id=3)  # 默认是and关系
# res = models.Book.objects.filter(Q(pk=1), Q(publish_id=3))  # 逗号分割还是and关系
res = models.Book.objects.filter(Q(pk=1) | Q(publish_id=3))  # 管道符是or关系
print(res)

2. ~ 波浪号not关系
res = models.Book.objects.filter(~Q(pk=1) | Q(publish_id=3))  # 波浪号是not关系
print(res)

(2)将查询条件的字段改为字符串形式
q_obj = Q()
q_obj.connector = 'or'  # Q对象默认的多个条件也是and关系,可以修改为or
q_obj.children.append(('pk', 1))
q_obj.children.append(('publish_id', 3))
res = models.Book.objects.filter(q_obj)
print(res)

5. ORM查询优化

5.1 特性

1. django orm默认都是惰性查询,当orm的语句在后续的代码中真正需要使用的时候才会执行
2. django orm自带limit分页,减轻数据库端以及服务端的压力

5.2 only与defer

only

1.拿到所有的书名
  res = models.Book.objects.all()
  for obj in res:
       print(obj.title)
2.res = models.Book.objects.values('title','price')
  for i in res:
     print(i.title)  # 字典不能通过点的方式
     print(i.get('title'))
  需求:实现i对象可以点title,price
3.res = models.Book.objects.only('title', 'price')
  for obj in res:
      print(obj.title)
      print(obj.price)
4.res = models.Book.objects.only('title', 'price')
  for obj in res
  需求:点其他字段,只不过每次都会走数据库查询
        print(obj.publish_time)


only会将括号内填写的字段封装成一个个数据对象 对象在点击的时候不会再走数据库查询
但是对象也可以点击括号内没有的字段 只不过每次都会走数据库查询

defer

res = models.Book.objects.defer('title', 'price')
for obj in res:
    print(obj.publish_time)


defer与only刚好相反 
    数据对象点击括号内出现的字段 每次都会走数据库查询
    数据对象点击括号内没有的字典 不会走数据库查询

select_related

1. res = models.Book.objects.all()
   for obj in res:
       print(obj.publish.name)  # 频繁走数据库
2. res = models.Book.objects.select_related('authors')
   for obj in res:
       print(obj.publish.name)
select_related括号内只能接收外键字段(一对多 一对一) 自动连表 得出的数据对象在点击表中数据的时候都不会再走数据库查询

prefetch_related

res = models.Book.objects.prefetch_related('publish')
for obj in res:
    print(obj.publish.name)

prefetch_related底层其实是子查询 将查询之后的结果也一次性封装到数据对象中 用户在使用的时候是感觉不出来的

6. 事务操作

事务:ACID(A:原子性  C:一致性  I:隔离性  D:持久性)
事务隔离级别:脏读、幻读,不可重复读、
原生SQL:start transaction\rollback\commit\savepoint 


from django.db import transaction
    try:
        with transaction.atomic():
            pass  # 多条ORM语句
    except Exception as e:
        print(e)

7. 模型层常见字段

7.1 常用字段

常用字段 定义
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()实例。
EmailField() 验证有效的E-mail地址
BooleanField() 传布尔值存数字0或1
TextField() 文本类型
CharField() 字符类型
FileField() 存储文件数据 自动找指定位置存储 字段存具体路径
ForeignKey() 外键
OneToOneField() 一对一
ManyToManyField() 多对多

7.2 对应关系

    '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)',

7.3 ORM自定义字段

class MyCharField(models.Field):
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super().__init__(max_length=max_length, *args, **kwargs)

    def db_type(self, connection):
        return 'char(%s)' % self.max_length
info = MyCharField(max_length=32)

7.4 ORM常见字段参数

字段参数 定义
null 用于表示某个字段可以为空。
unique 如果设置为unique=True 则该字段在此表中必须是唯一的 。
db_index 如果db_index=True 则代表着为此字段设置索引。
default 为该字段设置默认值。
auto_now_add 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
auto_now 配置上auto_now=True,每次更新数据记录的时候会更新该字段。
to 设置要关联的表
to_field 设置要关联的表的字段
on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。

1、models.CASCADE
    级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除
2、models.SET_NULL
    当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
3、models.PROTECT
    当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
4、models.SET_DEFAULT
    当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
5、models.SET()
    当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
6、models.DO_NOTHING
    什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似

8. 多对多三种创建方式

8.1 自动创建

authors = models.ManyToManyField(to='Author')

优点:第三张表自动创建
缺点:第三张表扩展性差

8.2 手动创建

class Book(models.Model):
   pass
class Author(models.Model):
   pass
class Book2Author(models.Model):
   book_id = models.ForeignKey(to="Book")
   author_id = models.ForeignKey(to="Author")

优点:第三张表扩展性强
缺点:无法使用正反向查询以及多对多四个方法

8.3 半自动创建

class Book(models.Model):
    authors = models.ManyToManyField(to='Author',
              through='Book2Author'
              through_fields=('book_id','author_id')
            )
class Author(models.Model):
     pass
 class Book2Author(models.Model):
     book_id = models.ForeignKey(to="Book")
     author_id = models.ForeignKey(to="Author")

优点:扩展性强并且支持正反向查询
缺点:无法使用多对多四个方法
posted @   |相得益张|  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示