您的位置:首页 > 编程语言 > Python开发

python Django 查询操作 聚合函数

2020-01-13 11:17 801 查看

python Django 查询操作

  • 2. 聚合函数
  • python Django 查询操作 聚合函数

    1. 查询操作

    查找是数据库操作中一个非常重要的技术。查询一般就是使用

    filter
    exclude
    以及
    get
    三个方法来实现。我们可以在调用这些方法的时候传递不同的参数来实现查询需求。在
    ORM
    层面,这些查询条件都是使用
    field + __ + condition
    的方式来使用的。以下将那些常用的查询条件来一一解释。

    (1)exact:精确查询 =

    如果提供的是一个

    None
    ,那么在
    SQL
    层面就是被解释为
    NULL

    示例代码如下:

    article = Article.objects.get(id__exact=14)
    article = Article.objects.get(id__exact=None)
    # 以上的两个查找在翻译为 SQL 语句为如下:
    select ... from article where id=14;
    select ... from article where id is NULL;

    (2)iexact:模糊查询 like

    在底层会被翻译成

    like

    • =
      like
      ,大部分情况下是等价的,只有少数部分是不等价的。
    • exact
      iexact
      ,他们的区别其实就是
      =
      like
      的区别,因为
      exact
      会被翻译成
      =
      ,而
      iexact
      会被翻译成
      like
    • 因为
      field_exact=xxx
      其实等价于
      filed=xxx
      ,因此我们直接使用
      filed=xxx
      ,并且因为大部分情况exact和iexact又是等价的,因此我们以后直接使用
      filed=xxx
      就可以了。

    示例代码如下:

    article = Article.objects.filter(title__iexact='hello world')
    # 那么以上的查询就等价于以下的 SQL 语句:
    select ... from article where title like 'hello world';

    (3)QuerySet.query/get

    query
    可以用来查看这个
    ORM
    查询语句最终被翻译成的
    SQL
    语句。
    但是
    query
    只能被用在
    QuerySet
    对象上,不能用在普通的
    ORM模型
    上。
    因此如果你的查询语句是通过
    get
    来获取数据的, 那么就不能使用
    query
    ,因为
    get
    返回的是满足条件的
    ORM
    模型,而不是
    QuerySet
    ,如果你是通过
    filter
    等其他返回
    QuerySet
    的方法查询的,那么就可以使用
    query

    (4)contains:大小写敏感

    使用大小写敏感的判断,某个字符串是否在指定的字段中,这个判断条件会使用大小写敏感,因此在被翻译或

    SQL
    语句的时候,会使用
    like binary
    ,而
    like binary
    就是使用大小写敏感的。

    示例代码如下:

    articles = Article.objects.filter(title__contains='hello')
    # 在翻译成 SQL 语句为如下:
    select ... where title like binary '%hello%';

    要注意的是,在使用 contains 的时候,翻译成的 sql 语句左右两边是有百分号的,意味着使用
    的是模糊查询。而 exact 翻译成 sql 语句左右两边是没有百分号的,意味着使用的是精确的查
    询。

    (5)icontains:大小写不敏感

    使用大小写不敏感的判断,某个字符串是否被包含在指定的字段中。
    这个查询语句在被翻译成

    SQL
    的时候,使用的是
    like
    ,而
    like
    MySQL
    层面就是不区分大小写的。

    示例代码如下:

    articles = Article.objects.filter(title__icontains='hello')
    # 在翻译成 SQL 语句为如下:
    select ... where title like '%hello%';

    (6)contains与icontains

    在被翻译成

    SQL
    的时候使用的是
    %hello%
    ,就是只要整个字符串中出现了
    hello
    都能够被找到,而
    iexact
    没有百分号,那么意味着只有完全相等的时候才会被匹配到。

    (7)in:提取值是否在容器中

    提取那些给定的 ‘field’ 的值是否在给定的容器中。容器可以为

    list
    tuple
    或者任何一个可以迭代的对象,包括
    QuerySet
    对象。

    示例代码如下:

    articles = Article.objects.filter(id__in=[1,2,3])
    # 以上代码在翻译成 SQL 语句为如下:
    select ... where id in (1,3,4)

    当然也可以传递一个

    QuerySet
    对象进去。

    示例代码如下:

    inner_qs = Article.objects.filter(title__contains='hello')
    categories = Category.objects.filter(article__in=inner_qs)
    # 以上代码的意思是获取那些文章标题包含 hello 的所有分类。
    # 将翻译成以下 SQL 语句,示例代码如下:
    select ...from category where article.id in (select id from article where title like '%
    hello%');

    (8)gt:大于

    某个 field 的值要大于给定的值。

    示例代码如下:

    articles = Article.objects.filter(id__gt=4)
    # 以上代码的意思是将所有 id 大于4的文章全部都找出来。
    # 将翻译成以下 SQL 语句:
    select ... where id > 4;

    (9)gte:大于等于

    类似于 gt ,是大于等于。

    (10)lt:小于

    类似于 gt 是小于。

    (11)lte:小于等于

    类似于 lt ,是小于等于。

    (12)startswith:以某个值开始(大小写敏感)

    判断某个字段的值是否是以某个值开始的。大小写敏感。

    示例代码如下:

    articles = Article.objects.filter(title__startswith='hello')
    # 以上代码的意思是提取所有标题以 hello 字符串开头的文章。
    # 将翻译成以下 SQL 语句:
    select ... where title like 'hello%'

    (13)istartswith:以某个值开始(大小写不敏感)

    类似于 startswith ,但是大小写是不敏感的。

    (14)endswith:以某个值结束(大小写敏感)

    判断某个字段的值是否以某个值结束。大小写敏感。

    示例代码如下:

    articles = Article.objects.filter(title__endswith='world')
    # 以上代码的意思是提取所有标题以 world 结尾的文章。
    # 将翻译成以下 SQL 语句:
    select ... where title like '%world';

    (15)iendswith:以某个值结束(大小写不敏感)

    类似于 endswith ,只不过大小写不敏感。

    (16)range:在给定的区间中

    判断某个 field 的值是否在给定的区间中。示例代码如下:

    from django.utils.timezone import make_aware # 新增
    from datetime import datetime
    start_date = make_aware(datetime(year=2018,month=1,day=1))
    end_date = make_aware(datetime(year=2018,month=3,day=29,hour=16))
    articles = Article.objects.filter(pub_date__range=(start_date,end_date))
    # 以上代码的意思是提取所有发布时间在 2018/1/1 到 2018/12/12 之间的文章。
    # 将翻译成以下的 SQL 语句:
    select ... from article where pub_time between '2018-01-01' and '2018-12-12'。

    需要注意的是,以上提取数据,不会包含最后一个值。也就是不会包含 2018/12/12 的文章。
    而且另外一个重点,因为我们在

    settings.py
    中指定了
    USE_TZ=True
    ,并且设置了
    TIME_ZONE='Asia/Shanghai'

    因此我们在提取数据的时候要使用
    django.utils.timezone.make_aware
    先将
    datetime.datetime
    navie
    时间转换为
    aware
    时间。
    make_aware
    会将指定的时间转换为
    TIME_ZONE
    中指定的时区的时间。

    (17)date:指定 date 的范围

    针对某些 date 或者 datetime 类型的字段。可以指定 date 的范围。并且这个时间过滤,还可以使用链式调用。

    示例代码如下:

    articles = Article.objects.filter(pub_date__date=date(2018,3,29))
    # 以上代码的意思是查找时间为 2018/3/29 这一天发表的所有文章。
    # 将翻译成以下的 sql 语句:
    select ... WHERE DATE(CONVERT_TZ(`front_article`.`pub_date`, 'UTC', 'Asia/Shanghai')) = 2018-03-29

    注意,因为默认情况下

    MySQL
    的表中是没有存储时区相关的信息的。
    因此我们需要下载一些时区表的文件,然后添加到 Mysql 的配置路径中。如果用的是
    windows
    操作系统。
    那么在
    http://dev.mysql.com/downloads/timezones.html
    下载
    timezone_2018d_posix.zip - POSIX standard

    然后将下载下来的所有文件拷贝到
    C:\ProgramData\MySQL\MySQL Server5.7\Data\mysql
    中,
    如果提示文件名重复,那么选择覆盖即可。

    如果用的是 linux 或者 mac 系统,那么在命令行中执行以下命令: mysql_tzinfo_to_sql
    /usr/share/zoneinfo | mysql -D mysql -u root -p ,然后输入密码,从系统中加载时区文件更新
    到 mysql 中。

    (18)year:根据年份进行查找

    示例代码如下:

    articles = Article.objects.filter(pub_date__year=2018)
    articles = Article.objects.filter(pub_date__year__gte=2017)
    以上的代码在翻译成 SQL 语句为如下:
    select ... where pub_date between '2018-01-01' and '2018-12-31';
    select ... where pub_date >= '2017-01-01';

    (19)month: 根据月份进行查找

    (20)day: 根据日期进行查找

    (21)week_day:根据星期几进行查找

    Django 1.11 新增的查找方式。同 year ,根据星期几进行查找。1表示星期天,7表示星期六, 2-6 代表的是星期一到星期五。

    (22)time:根据时间进行查找

    =

    示例代码如下:

    articles = Article.objects.filter(pub_date__time=datetime.time(12,12,12));

    以上的代码是获取每一天中12点12分12秒发表的所有文章。
    更多的关于时间的过滤,
    请参考 Django 官方文
    档: https://docs.djangoproject.com/en/2.0/ref/models/querysets/#range

    (23)isnull:根据值是否为空进行查找

    示例代码如下:

    articles = Article.objects.filter(pub_date__isnull=False)
    # 以上的代码的意思是获取所有发布日期不为空的文章。
    # 将来翻译成 SQL 语句如下:
    select ... where pub_date is not null;

    (24)regex和iregex:正则表达式

    大小写敏感和大小写不敏感的正则表达式。

    示例代码如下:

    articles = Article.objects.filter(title__regex=r'^hello')
    # 以上代码的意思是提取所有标题以 hello 字符串开头的文章。
    # 将翻译成以下的 SQL 语句:
    select ... where title regexp binary '^hello';

    iregex 是大小写不敏感的。
    根据关联的表进行查询:
    假如现在有两个 ORM 模型,一个是 Article ,一个是 Category 。

    示例代码如下:

    class Category(models.Model):
    """文章分类表"""
    name = models.CharField(max_length=100)
    class Article(models.Model):
    """文章表"""
    title = models.CharField(max_length=100,null=True)
    category = models.ForeignKey("Category",on_delete=models.CASCADE)
    比如想要获取文章标题中包含"hello"的所有的分类。那么可以通过以下代码来实现:
    categories = Category.object.filter(article__title__contains("hello"))

    2. 聚合函数

    模版对象,示例代码如下:

    from django.db import models
    
    class Author(models.Model):
    """作者模型"""
    name = models.CharField(max_length=100)
    age = models.IntegerField()
    email = models.EmailField()
    
    class Meta:
    db_table = 'author'
    
    class Publisher(models.Model):
    """出版社模型"""
    name = models.CharField(max_length=300)
    
    class Meta:
    db_table = 'publisher'
    
    class Book(models.Model):
    """图书模型"""
    name = models.CharField(max_length=300)
    pages = models.IntegerField()
    price = models.FloatField()
    rating = models.FloatField()
    author = models.ForeignKey(Author,on_delete=models.CASCADE)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
    
    class Meta:
    db_table = 'book'
    
    class BookOrder(models.Model):
    """图书订单模型"""
    book = models.ForeignKey("Book",on_delete=models.CASCADE)
    price = models.FloatField()
    
    class Meta:
    db_table = 'book_order'

    (1)Avg—平均值

    (1)所有聚合函数都是放在

    django.db.models
    下面。

    (2)聚合函数不能够单独的执行,需要放在一些可以执行聚合函数的方法下面中去执行。比如

    aggregate

    示例代码如下:

    result = Book.objects.aggregate(Avg('price'))

    (3)聚合函数执行完成后,给这个聚合函数的值取个名字,取名字的规则,默认是

    filed+__+聚合函数名字
    形成的。

    比如以上代码形成的名字叫做

    price__avg
    。如果不想使用默认的名字,那么可以在使用聚合函数的时候传递关键字参数进去,参数的名字就是聚合函数执行完成的名字。

    示例代码如下:

    result = Book.objects.aggregate(avg=Avg('price'))

    以上传递了关键字参数

    avg=Avg(“price”)
    ,那么以后
    Avg
    聚合函数执行完成的名字就叫做
    avg

    (4)aggregate :这个方法不会返回一个

    QuerySet
    对象,而是返回一个字典。这个字典中的
    key
    就是聚合函数的名字,值就是聚合函数执行后的结果。

    (2)Count:获取个数

    示例代码如下:

    from django.db.models import Count
    result = Book.objects.aggregate(book_num=Count('id'))

    以上的

    result
    将返回
    Book
    表中总共有多少本图书。
    Count
    类中,还有另外一个参数叫做
    distinct
    ,默认是等于
    False
    ,如果是等于
    True
    , 那么将 去掉那些重复的值 。比如要获取作者表中所有的不重复的邮箱总共有多少个,

    示例代码如下:

    from djang.db.models import Count
    result = Author.objects.aggregate(count=Count('email',distinct=True))

    (3) Max 和 Min

    获取指定对象的最大值和最小值。比如想要获取

    Author
    表中,最大的年龄和最小的年龄分别是多少。

    示例代码如下:

    from django.db.models import Max,Min
    result = Author.objects.aggregate(Max('age'),Min('age'))
    # 如果最大的年龄是88,最小的年龄是18。那么以上的result将为:
    {"age max":88,"age min":18}

    (4) Sum

    求指定对象的总和。比如要求图书的销售总额。

    from djang.db.models import Sum
    result = Book.objects.annotate(total=Sum("bookstore price")).values("name","total")

    以上的代码

    annotate
    的意思是给
    Book
    表在查询的时候添加一个字段叫做
    total
    ,这个字段的数据来源是从
    BookStore
    模型
    price
    的总和而来。
    values
    方法是只提取
    name
    total
    两个字段的值。

    更多的聚合函数请参考官方文档:
    https://docs.djangoproject.com/en/2.0/ref/models/querysets/#aggregation-functions

    (5)aggregate和annotate的区别:aggregate返回所有字段和值;annnotate返回指定字段和值

    1. aggregate
      :返回使用聚合函数后的字段和值。

    2. annotate
      :在原来模型字段的基础之上添加一个使用了聚合函数的字段,并且在使用聚合函数的时候,会使用当前这个模型的主键进行分组(
      group by
      )。
      比如以上
      Sum
      的例子,如果使用的是
      annotate
      ,那么将在每条图书的数据上都添加一个字段叫做
      total
      ,计算这本书的销售总额。
      而如果使用的是
      aggregate
      ,那么将求所有图书的销售总额。

    (6)F表达式:用来优化 ORM 操作数据库的

    F表达式
    是用来优化 ORM 操作数据库的。
    比如我们要将公司所有员工的薪水都增加1000元,如果按照正常的流程,应该是先从数据库中提取所有的员工工资到Python内存中,然后使用Python代码在员工工资的基础之上增加1000元,最后再保存到数据库中。这里面涉及的流程就是,首先从数据库中提取数据到Python内存中,然后在Python内存中做完运算,之后再保存到数据库中。

    示例代码如下:

    employees = Employee.objects.all()
    for employee in employees:
    employee.salary += 1000
    employee.save()

    而我们的 F表达式 就可以优化这个流程,他可以不需要先把数据从数据库中提取出来,计算完成后再保存回去,他可以直接执行 SQL语句 ,就将员工的工资增加1000元。

    示例代码如下:

    from djang.db.models import F
    Employee.object.update(salary=F("salary")+1000)

    F表达式 并不会马上从数据库中获取数据,而是在生成 SQL 语句的时候,动态的获取传给 F表达式 的值。
    比如如果想要获取作者中, name 和 email 相同的作者数据。如果不使用 F表达式 ,

    示例代码如下:

    authors = Author.objects.all()
    for author in authors:
    if author.name == author.email:
    print(author)

    如果使用 F表达式 ,那么一行代码就可以搞定。

    示例代码如下:

    from django.db.models import F
    authors = Author.objects.filter(name=F("email"))

    (7)Q表达式:“|”:或,“~”:非、“&”:并

    如果想要实现所有价格高于100元,并且评分达到9.0以上评分的图书。

    示例代码如下:

    books = Book.objects.filter(price__gte=100,rating__gte=9)

    以上这个案例是一个并集查询,可以简单的通过传递多个条件进去来实现。
    但是如果想要实现一些复杂的查询语句,比如要查询所有价格低于10元,或者是评分低于9分的图书。
    那就没有办法通过传递多个条件进去实现了。这时候就需要使用 Q表达式 来实现了。

    示例代码如下:

    from django.db.models import Q
    books = Book.objects.filter(Q(price__lte=10) | Q(rating__lte=9))

    以上是进行或运算,当然还可以进行其他的运算,比如有 & 和 ~(非) 等。

    一些用 Q 表达式的例子如下:
    “|”:或
    “~”:非
    “&”:并

    from django.db.models import Q
    # 获取id等于3的图书
    books = Book.objects.filter(Q(id=3))
    # 获取id等于3,或者名字中包含文字"记"的图书
    books = Book.objects.filter(Q(id=3)|Q(name__contains("记")))
    # 获取价格大于100,并且书名中包含"记"的图书
    books = Book.objects.filter(Q(price__gte=100)&Q(name__contains("记")))
    # 获取书名包含“记”,但是id不等于3的图书
    books = Book.objects.filter(Q(name__contains='记') & ~Q(id=3))
    • 点赞
    • 收藏
    • 分享
    • 文章举报
    python-学者 发布了24 篇原创文章 · 获赞 0 · 访问量 522 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: