53、聚合查询、分组查询、F与Q查询、Django开启事务、模型层中常见的字段和参数、ORM字段参数、自定义字段、图书管理系统作业讲解

166 阅读5分钟

聚合查询(aggregate)

1.在mysql中,聚合函数有:sum min max avg count
2.在orm中如何使用聚合函数
	1.必须要有有关键字:aggregate
  2.我们需要导模块:from django.db.models import Count, Sum, Max, Min, Avg
3. 返回的值是字典,并且aggregate()方法里可以查询多个 

需求:求书籍表中得书的平均价格

from django.db.models import Avg
res = models.Book.objects.aggregate(Avg('price'))
print(res)

from django.db.models import Avg,Max,Min,Sum,Count
res = models.Book.objects.aggregate(Avg('price'),Max('price'),Min('price'),Count('pk'),Sum('price'))
print(res)  # {'price__avg': 6888.2, 'price__max': Decimal('22222.00'), 'price__min': Decimal('666.00'), 'pk__count': 5, 'price__sum': Decimal('34441.00')}

分组查询(annotate)

1.在mysql中,使用分组是用group by,并且分组之后只能取得分组的依据,其他的字段不能拿到(严格模式),如果不设置这个模式,所有字段都可以都可以取到
2.设置一个严格模式:
	第一种:
  	set global sql_mode='only_full_group_by' 
   第二种:在配置文件中修改
    my.ini-----mac系统(my.cnf)
    sql_mode='only_full_group_by'
 		重启Mysql的服务生效
3.在orm中如何进行分组 ---- 使用annotate()
		ps:聚合函数一般与分组配合使用
  
4.按照表达的其他字段分组
res = models.Book.objects.values('').annotate()

5.如果在分组的时候,宝座了,很有可能是严格模式的问题,--->把严格模式去掉

1.统计每一本书的作者个数

from django.db.models import Count
res = models.Book.objects.annotate(authors_num=Count('authors__pk')).values('title','authors_num')
print(res)  # <QuerySet [{'title': '自传1', 'authors_num': 2}, {'title': '自传5', 'authors_num': 0}, {'title': '自传4', 'authors_num': 0}, {'title': '自传2', 'authors_num': 2}, {'title': '自传3', 'authors_num': 0}]>

2.统计每个出版社卖的最便宜的书的价格

from django.db.models import Min
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res)  # <QuerySet [{'name': '清华出版社', 'min_price': Decimal('666.00')}, {'name': '人民出版社', 'min_price': Decimal('22222.00')}, {'name': '新华出版社', 'min_price': Decimal('777.00')}]>

3.统计不止一个作者的图书

res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title','author_num')
print(res)

4.查询每个作者出的书的总价格

from django.db.models import Sum
res = models.Author.objects.annotate(price_sum=Sum('book__price')).values('name','price_sum')
print(res)  # <QuerySet [{'name': 'nana', 'price_sum': Decimal('1443.00')}, {'name': 'cx', 'price_sum': Decimal('1443.00')}, {'name': 'xiao', 'price_sum': None}]>

F查询

1.F查询就是拿原来的值
2.用的书后需要导模块:from django.db.models import F
3.拼接(Concat)--- 导模块-->from django.db.models.functions import Concat

1.查询卖出数大于库存数的书籍

from django.db.models import F
res = models.Book.objects.filter(sale_num__lt=F('kucun')).values('title')
print(res)  # <QuerySet [{'title': '自传3'}, {'title': '自传5'}]>

2.将所有书籍的价格提升500块

from django.db.models import F
models.Book.objects.update(price = F('price')+500)

3.将所有书的名称后面加上爆款两个字

1.错误写法:
  update app01_book set title = title + 'haha'
  models.Book.objects.update(title=F('title')+'haha'
2.正确
    from django.db.models.functions import Concat
    from django.db.models import F
    from django.db.models import Value
    models.Book.objects.update(title = Concat(F('title'),Value('_nb')))

Q查询

对于filter()方法内逗号分隔开的多个条件,都是and关系,如果想用or或者not关系,则需要使用Q,器其解决的是逻辑关系(and,or,not)

1.查询卖出数大于200或者价格小于600的书籍

from django.db.models import Q
res = models.Book.objects.filter(Q(sale_num__gt=200)|Q(price__lt=600)).values('title')
print(res)

ps:
  res = models.Book.objects.filter(sale_num__gt=100, price__lt=600) # and关系
  res = models.Book.objects.filter(sale_num__gt=100).filter(price__lt=600) # and关系
  res = models.Book.objects.filter(Q(sale_num__gt=100), Q(price__lt=600))  # and关系
  res = models.Book.objects.filter(~Q(sale_num__gt=100)|Q(price__lt=600))  # ~是非的关系

Q补充高级用法

能够以字符串作为查询字段

源码:
class Q(tree.Node):
    AND = 'AND'
    OR = 'OR'
    default = AND
    def __init__(self, *args, **kwargs):
        super(Q, self).__init__(children=list(args) + list(kwargs.items()))
        
# 鼠标点进children里
class Node(object):
    default = 'DEFAULT'
    def __init__(self, children=None, connector=None, negated=False):
        self.children = children[:] if children else []
        self.connector = connector or self.default
        self.negated = negated
    ''''''

# 筛选出对浏览器端对按价格排序的数据
requests.GET.get('sort') # ‘price’
res = models.Book.objects.filter(price__gt=100)
res = models.Book.objects.filter('price'+'__gt'=100)
1.q = Q()
2.q.connector = 'or' # 把多个查询条件改为OR关系了
3.q.children.append(('maichu__gt', 100))
4.q.children.append(('sale_num__lt',100))
res = models.Book.objects.filter(q)  # 默认and关系 ,,
print(res)

Django中如何开启事务

1.在mysql中的事务ACID:原子性、一致性、隔离性、持久性
2.事务的作用:保证数据安全的,写在事务里面的sql语句要么全部成功,要么全部失败
3.Mysql的隔离级别:脏读、重复读
4.开启事务的步骤
    1.开启事务:start transaction
    2.提交事务:commit
    3.回滚事务:rollback
5.在django中如何开启事务
    from django.db import transaction
    try:
        with transaction.atomic():  # 开启
            models.Book.objects.create()
            models.Publish.objects.update()
            # sql1
            # sql2
            ...
    except Exception as e:
        print(e)  # 当sql语句出现异常的时候,可以在这里打印错误信息
        transaction.rollback()  # 回滚

模型层中常见字段和参数

1.AutoField:int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。

2.IntegerField:一个整数类型,范围在 -2147483648 to 21474836473.CharField:字符类型,必须提供max_length参数, max_length表示字符长度。

4.DateField:日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。(auto_now=True,auto_now_add=True)

5.DateTimeField:日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例

6.BooleanField(Field)- 布尔值类型(性别--tinyint)
	在代码里面就是要True/False------------>在数据库中存的是0/1
  
7.TextField(Field)- 文本类型(存大段文本)
		可以存大段的文本---->当字符串比较少的时候,一般用varchar(512)
  
8.FileField(Field)- 字符串,路径保存在数据库,文件上传到指定目录
    - 参数:
    upload_to = ""      上传文件的保存路径
    storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
    
9.image = models.FileField(upload_to='文件上传的位置')
image = models.ImageField(upload_to='图片')  # 只能上传图片

10.FloatField(Field)- 浮点型

11.DecimalField(Field)
    - 10进制小数
    - 参数:
    max_digits,小数总长度
    decimal_places,小数位长度
    
其他:https://www.yuque.com/liyangqit/lb35ya/cmatgb

ORM字段参数

1.null:用于表示某个字段可以为空。

2.unique:如果设置为unique=True 则该字段在此表中必须是唯一的 。

    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
		# 等同于:
    author_detail = models.ForeignKey(unique=True, to='AuthorDetail', on_delete=models.CASCADE)
    
3.db_index:如果db_index=True 则代表着为此字段设置索引。

4.default:为该字段设置默认值。

5.DateField和DateTimeField
    配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
    配置上auto_now=True,每次更新数据记录的时候会更新该字段。
    
6.verbose_name:Admin中显示的字段名称

自定义字段

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)