django查询数据和orm优化
聚合查询
'聚合查询通常情况下都是配合分组一起使用的'
# 聚合函数查询关键字
aggregate
# 聚合函数
Max		: 最大值
Min		: 最小值
Sum		: 求合
Count	: 计数
Avg		: 平均值
# 聚合函数的使用
from app01 import models
from django.db.models import Max,Min,Sum,Count,Avg
1.查询所有书籍的最高价格
res = models.Book.objects.aggregate(Max('price'))
'''{'price__max': Decimal('5646.21')}'''
# 聚合函数的一致使用
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Count('pk'), Avg('price'))
print(res)
'''{'price__max': Decimal('5646.21'), 'price__min': Decimal('222.21'), 'price__sum': Decimal('7209.17'), 'pk__count': 5, 'price__avg': 1441.834}'''
分组查询
# 分组查询
annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数,所有使用前要从django.db.models引入Avg,Max,Count,Sum(首字母大写))
# 返回值
分组后,用values取值,则返回值是QuerySet书籍类型里面为一个个字典
分组后,用values_list取值,则返回值是QuerySet数据类型里面为一个个元组
'MySQL中的limit相当于ORM中的QuerySet数据类型的切片'
# 分组查询的特点
values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字段分组,annotate 执行分组
values 或者 values_list 放在annotate后面: annotate 表示直接以当前表的pk执行分组,values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 values 或者 values_list 里写其别名
filter放在 annotate 前面:表示where条件
filter放在annotate后面:表示having
'''ORM执行分组操作 如果报错 可能需要去修改sql_mode 移除only_full_group_by'''
分组查询练习
from django.db.models import Max, Min, Sum, Count, Avg
# 统计每本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
# 统计每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
# 统计不止一个作者的图书
res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title','author_num')
# 统计每个作者出的书的总价格
res = models.Author.objects.annotate(book_sum_price=Sum('book__price')).values('name','book_sum_price')
'以表中的某个字段分组如何操作'
models.Author.objects.values('age').annotate()
# 统计每个出版社主键值对应的书籍个数
res = models.Book.objects.values('publish_id').annotate(book_num=Count('pk')).values('publish_id','book_num')
迁移数据
# 当表中已经有数据的情况下 添加额外的字段 需要指定默认值或者可以为null
方式1
IntegerField(verbose_name='销量',default=1000)
方式2
IntegerField(verbose_name='销量',null=True)
方式3
在迁移命令提示中直接给默认值
F与Q查询
F查询
# F查询的作用
能够帮助你直接获取到列表中某个字段对应的数据
# 查询库存大于销量的书籍
from django.db.models import F
res = models.Book.objects.filter(kucun__gt=F('maichu'))
# 将所有书的价格提升500
res = models.Book.objects.update(price=F('price') + 500)
'在操作字符串类型的数据的时候, F不能够直接做到字符串的拼接'
# 将所有书的名称后面加上_爆款后缀
from django.db.models.functions import Concat
from django.db.models import Value
res = models.Book.objects.update(name=Concat(F('title'), Value('爆款')))
Q查询
# Q查询的作用
filter括号内多个条件默认是and关系 无法直接修改
使用Q对象 就可以支持逻辑运算符
# 查询价格大于20000或者卖出大于1000的书籍
from django.db.models import Q
',逗号是and关系'
res = models.Book.objects.filter(Q(price__gt=20000), Q(maichu__gt=1000))  
'|管道符是or关系'
res = models.Book.objects.filter(Q(price__gt=20000) | Q(maichu__gt=1000))  
'~波浪符是not操作'
res = models.Book.objects.filter(~Q(price__gt=20000)) 
Q查询进阶用法
'能够将查询条件的左边也变成字符串的形式 而 不是变量形式'
'children里面添加的是元组'
q_obj = Q() # 先产生一个空对象  实例化
q_obj.connector = 'or'  # 默认是and 可以改为or
'q对象里面有一个children'
q_obj.children.append(('maichu__gt', 100))
# 第一个元素就会被当作查询条件的左边 第二个元素会被当作查询条件右边
q_obj.children.append(('price__lt', 600))
res = models.Book.objects.filter(q_obj)  # filter 除了可以放条件 还可以放对象
print(res.query)

ORM查询优化
# ORM语句特点
1.orm查询默认都是惰性查询(能不消耗数据库资源就不消耗)
光编写orm语句并不会直接指向SQL语句 只有后续的代码用到了才会执行
2.orm查询默认自带分页功能(尽量减轻单次查询数据的压力)
only
# only的用法
会产生对象结果集 对象点括号内出现的字段不会再走数据库查询
但是如果点击了括号内没有的字段也可以获取到数据 但是每次都会走数据库查询
res = models.Book.objects.only('title')
for i in res:
    
print(i.title)  '点only括号内的字段 不会走数据库'
print(i.price)   '点only括号内没有的字段 会重新走数据库查询 ' 


defer
# defer的用法
defer与only刚好相反 对象点括号内出现的字段会走数据库查询
如果点击了括号内没有的字段也可以获取到数据 每次都不会走数据库查询
res = models.Book.objects.defer('title')
for i in res:
    print(i.title)
'''对象除了没有title属性之外 其他的都有,除了title走数据库,其他都不需要走数据库'''
# select_related
1.select_related内部直接先将book与publish连接起来
2.然后一次性将大表里面的所有数据全部封装给查询出来的对象
3.这个时候对象无论是点击book表数据还是publish的数据都无需再走数据库查询了
'select_related括号内只能传一对一和一对多字段 不能传多对多字段'
res = models.Book.objects.select_related('publish')  # INNER JOIN
for i in res:
print(i.publish.name)
# prefetch_related
prefetch_related该方法内部其实就是子查询
将子查询查询出来的所有结果也给你封装到对象中
给你的感觉好像也是一次性搞定的
res = models.Book.objects.prefetch_related('publish')  # 子查询
for i in res:
print(i.publish.name)
'''
# 总结
select_related:
	相当于是INNER JOIN 将两张表拼接起来
prefetch_related:
	相当于是 子查询 分布查询
'''
ORM常见字段
# AutoField   	int自增列
int自增列,必须填入参数primary_key=True
当model中如果没有自增列,则自动会创建一个列名为id的列
# CharField		varchar
verbose_name	字段的注释
max_length		字符长度
# IntegerField	int
BigIntegerField			bigint
# DecimalField	decimal
max_digits=8		总共位数
decimal_places=2	小数位
#EmailFiled		varchar(254)
# DateField					date
日期字段,日期格式  YYYY-MM-DD
相当于Python中的datetime.date()
#BooleanField(Field)		 布尔值类型
该字段传布尔值(False/True) 	数据库里面存0/1
#TextField(Field)			 文本类型
存储大段文本 没有字数限制
#FileField()				 字符类型
传文件自动保存到指定位置并存文件路径
自定义字段类型
'django除了提供了很多字段类型之外 还支持你自定义字段'
class MyCharField(models.Field): # 字段类内部都继承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
    
# 自定义字段使用
myfield = MyCharField(max_length=16,null=True)  # 可以为空
ORM重要参数
# unique=True
	ForeignKey(unique=True)   等价于   OneToOneField()
# db_index
	如果db_index=True 则代表着为此字段设置索引
# to_field
	设置要关联的表的字段  默认不写关联的就是另外一张的主键字段
# on_delete
	当删除关联表中的数据时,当前表与其关联的行的行为
  """
  django2.X及以上版本 需要你自己指定外键字段的级联更新级联删除
  """
# default
为该字段设置默认值。
# auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库
# auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段
# choices
用于可以被列举完全的数据
eg:性别 学历 工作经验 工作状态
    class User(models.Model):
          username = models.CharField(max_length=32)
          password = models.IntegerField()
          gender_choice = (
              (1,'男性'),
              (2,'女性'),
          )
          gender = models.IntegerField(choices=gender_choice)
				user_obj.get_gender_display()  
        # 有对应关系就拿 没有还是本身
"""
1.字段存什么,根据gender_choices内元组的第一个元素,
2.元素如果是数字就放一个数字类型的字段
3.元素如果是字符串就放一个字符串类型的字段
""" 
# 验证 choices取值对应关系
user_obj = models.User.objects.filter(pk=1).first()
print(user_obj.get_gender_display())
'如果没有对应关系 那么字段是什么就展示什么 不会报错'

事务操作
# 事务的四大特征(ACID)
A:	原子性
	每个事务都是不可分割的最小单位(同一个事物内的多个操作要么同时成功要么同时失败)
C:	一致性
	事物必须是使数据库从一个一致性状态编导另一个一致性状态,一致性与原子性使密切相关的
I:	独立性
	事物与事物之间彼此不干扰
D:	持久性
	一个事物一旦开启,它对数据库中书籍的改变就应该使永久性的
    
# 开启事务
1.transaction 开启事务
2.rollback 回滚
3.commit 确认
from django.db import transaction
    try:
        with transaction.atomic():
            pass
    except Exception:
        pass
ORM执行原生SQL
# 方式1
from django.db import connection, connections
cursor = connection.cursor()  
cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
cursor.fetchone()
# 方式2
models.UserInfo.objects.extra(
                    select={'newid':'select count(1) from app01_usertype where id>%s'},
                    select_params=[1,],
                    where = ['age>%s'],
                    params=[18,],
                    order_by=['-age'],
                    tables=['app01_usertype']
                )
多对多三种创建方式
全自动创建
'利用orm自动帮我们创建第三张关系表'
class Book(models.Model):
    name = models.CharField(max_length=32)
    authors = models.ManyToManyField(to='Author')
    
# 全自动创建优缺点   
优点: 代码不需要自己写 非常方便 还支持orm提供操作第三张关系表的方法
    
缺点: 第三张关系表的扩展性极差(没办法额外添加字段)
纯手动创建
class Book(models.Model):
    name = models.CharField(max_length=32)
class Author(models.Model):
    name = models.CharField(max_length=32)
class Book2Author(models.Model):
    book_id = models.ForeignKey(to='Book')
    author_id = models.ForeignKey(to='Author')
    
# 纯手动创建优缺点 
优点: 第三张表完全取决你自己进行额外的扩展
    
缺点: 需要写的代码较多,不能再使用orm提供的简单的方法
半自动创建
'可以使用orm的正反向查询 但是没法使用add,set,remove,clear这四个方法'
class Book(models.Model):
    title = models.CharField(max_length=32)
    authors = models.ManyToManyField(
      					to='Author',
    					through='Book2Author',  # 指定表
      					through_fields=('book','author')  # 指定字段
    )
  class Author(models.Model):
    name = models.CharField(max_length=32)
    '''多对多建在任意一方都可以 如果建在作者表 字段顺序互换即可'''
    books = models.ManyToManyField(
      					to='Author',
    						through='Book2Author',  # 指定表
      					through_fields=('author','book')  # 指定字段
    )
  class Book2Author(models.Model):
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')
    
#through 
告诉orm我跟作者是多对多的关系 我是通过我自己写的表创建关系的 该表Book2Author 创建关系
#through_fields
告诉orm书籍跟作者是通过book,author字段来绑定关系的
