16.Django(基于双下划线的进阶查询、related_name、聚合查询、分组查询\F查询、Q查询、orm执行原生sql)

双下划线的进阶查询

  • 三张表的进阶查询

    需求:查询指定出版社的书籍名称和作者名字

    Publish、Book、Author涉及三张表,我们就有三种方式。涉及多少张表,我们就有多少种方式去做。

    • 正向查询

      obj = models.Book.objects.filter(Publish__name='xx出版社').values(
      'title',
      'Author__name',
      )
    • 反向查询

      # 方式1:
      obj = models.Publish.object.filter(name='xx出版社').values(
      'book__title',
         'book__author__name',
      )
      ​
      ​
      # 方式2:
      obj = models.Author.objects.filter(book__Publish__name='IT出社').values(
         'book__title',
         'name',
      )
      print(obj)
  • 四张表的进阶查询

    需求:手机号为166开头的作者出版的所有书籍名称以及出版社名称

    AuthorDetail、Author、Book、Publish

    • 正向查询

      # 方式1:
      obj = models.Author.objects.filter(AuthorDetail__telephone__startswith='166').values(
      'book__title',
         'book__Publish__name'
      )
      ​
      ​
      ​
      # 方式2:
      obj = models.Book.objects.filter(Author__AuthorDetail__telephone__startswith='166').values(
         'title',
         'Publish__name',
      )
      print(obj)
    • 反向查询

      # 方式1:
      obj = models.AuthorDetail.objects.filter(telephone__startswith='166').values(
      'author__book__title',
         'author__book__Publish__name',
      )
      print(obj)
      ​
      ​
      # 方式2:
      obj = models.Publish.objects.filter(book__Author__AuthorDetail__telephone__startswith='166').values(
         'book__title',
         'name',
      )
      print(obj)

 

related_name (了解)

面试曾经考过:related_name是什么?用在哪里?

基于对象的查询如果我们进行反向查询需要获取关联表的表名;基于双下划线的连表查询我们在连表的时候,也需要获取表的名字__。

'''
测试:
我们给book表中的publish字段加一个此属性
'''
Publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, related_name='xx')
​
# 重新makemirations、migrate
​
​
​
​
# 测试基于对象的查询
​
# 正向查询:需求:查询指定书的出版社
obj = models.Book.objects.get(title='指定的书名')
print(obj.publish.name)  # related_name 不影响
​
# 反向查询:需求:查询指定出版社出版的书名
obj = models.Publish.objects.get(name='指定出版社')
# print(obj.book_set.all()) # 查不到
print(obk.xx.all())
​
related_name 这个属性替代了反向查询时需要 类名_set 这个方法
​
​
​
​
# 基于双下划线连表的操作
​
# 正向查询:需求:查询指定书的出版社
obj = models.Book.objects.filter(title='指定书').values(
'publish_name',
)
print(obj)
​
# 反向查询:需求:查询指定书的出版社
# obj = models.Publish.objects.filter(book__title='指定书名').values(
# 'publihs__name',
# )
# print(obj)   # 报错
​
obj = models.Publish.objects.filter(xx__title='指定书名').values(
   'publish__name',
)
print(obj)

小结:

  • related_name只是适用于反向查询

  • 基于对象那个的反向查询时需要类名_set这种方法替换成了你设置的related_name就行了;基于双下划线的反向查询将类名__字段替换成related_name__字段。

 

 

 

聚合查询:

mysql中有什么聚合函数?

统计:count()
最大:max()
最小:min()
求和:sum()
平均值:avg()
​
聚合字段:concat()

orm也是支持这些聚合函数的

需要先引入

from django.db.models import Max,Min,Avg,Sum,Count

使用聚合查询需要使用aggregate(),在括号里面写聚合函数

# 注意:聚合函数导入和使用时,开头字母要大写
​
from django.db.models import Max,Min,Avg,Sum,Count
num = models.Book.objects.aggregate(Sum('price'))
print(num)

aggregate()这个方法返回的是一个字段,返回一个字段就意味着这句话终结了。结束了。

 

 

分组查询:

将同类的分成一组统计或者计算一些内容,一般都是找重复性较高的字段进行分组。在orm中有两种方式(一般多用于第二种)

  • 方式1:

    需求:查询每个出版社的书籍的平均价格

    # 原生mysql语句
    select avg(price) from app01_book group by publish_id;
    # orm 语句
    res = models.Book.objects.values('Publish_id').annotate(a=Avg('price'))
    # 这里的values加分组的字段也就是原生mysql语句中的group by。
    # annotate里面放置的就是分组之后要统计的内容。
    print(res)

    方式1的优点:可以进行多条件的分组

    res = models.Book.objects.values('publish_id','id').annotate(a=Avg('price'))    print(res)

    方式1可以进行having操作1也可以进行连表操作,还可以加多个条件

    res = models.Book.objects.values('publish__id').annotate(a=Avg('price')).fil ter(a__gt=100)
    print(res)

     

  • 方式2:

    需求:拆线呢每个出版社出版的书籍的平均价格

    原理是基于双下划线连表操作的

    res = models.Publish.objects.annotate(a=Avg('book__price'))
    print(res)
       
    res = models.Publish.objects.annotate(a=Avg('book__price')).values('name','a')
    print(res)

    优点:在此种写法基础上,可以进行进一步的筛选

    需求:查询每个出版社出版的书籍的平均价格大于100的

    res = models.Publish.objects.annotate(a=Avg('book_price')).valeus('name','a').filter(a__gt=100)
    # filter 充当的是having的作用
    print(res)

 

F查询

F查询主要用于单表的查询

给book表加上两个字段,评论comment,点赞good

class Book(models.Model):
   title = models.CharField(max_length=32)
   publishDate = models.DateField()
   comment = models.IntegerField(default=0)
   good = models.IntegerField(default=0)
   price = models.DecimalField(max_digits=5, decimal_places=2)
   Author = models.ManyToManyField(to='Author')
# 在Terminal中输入命令
python manage.py makemigrations
python manage.py migrate

使用前需要导入模块

from django.db.models import Max, Min, Sum, Avg, Count, F, Q
  • 字段之间的比较

    需求:查询评论数比点赞数多的书籍

    此时我们要通过F查询进行筛选

    from django.db.models import Max, Min, Sum, Avg, Count, F, Q
    ​
    qs = models.Book.objects.filter(comment__gt=F('good'))
    print(qs)
  • 可以支持四则运算(+、-、*、/)

    需求:评论数比点在数多50票以上的

    qs = models.Book.objects.filter(comment__gt=F('good')+50)
    print(qs)
  • 整个字段的批量操作

    需求:所有书籍的价格都增加20元

    models.Book.objects.all().update(price=F('price')+20)
  • 小结:

    单表字段之间的比较以及批量操作时,用到F查询

 

Q查询

使用前需要导入模块

from django.db.models import Max, Min, Sum, Avg, Count, F, Q
  • 可以充当filter()查询条件中的 ’或‘ 使用,通过 | 连接

    需求:查询所有书籍中评论数大于200的或者点赞数大于200的书籍

    qs = models.Book.objects.filter(Q(comment__gt=200) | Q(good__gt=200))
    print(qs)
  • Q查询还可以进行取反、与 的操作

    需求:查询所有书籍中价格在100以下并且评论数在300以上的书籍。

    qs = models.Book.objects.filter(~Q(price__gt=100) & Q(comment__gt=300))
    print(qs)
    ~ :代表取反
    & :代表与
    ​
    # 简写:
    qs = models.Book.objects.filter(prict__lt=100, comment__gt=300)
    print(qs)
  • 支持嵌套操作

    需求:查询价格大于200的,评论数或者点赞数大于100的书籍

    qs = models.Book.objects.filter(Q(price__gt=200) & Q(Q(comment__gt=100) | Q(good__gt=100)))
    print(obj)
  • Q查询必须放在普通查询前面

    需求:查询价格大于200的,评论数或者点赞数大于100的书籍

    qs = models.Book.objects.filter(Q(Q(comment__gt=100)|Q(good__gt=100)),prict__gt=200)
    print(qs)
  • &的优先级比 |的优先级高

 

ORM执行原生sql语句(了解)

orm不能映射所有的sql语句,特别复杂的sql语句你用orm是写不出来的,那么我们据需要用sql语句查询数据。

我们之前使用的pymysql去执行的原生sql。但是orm给我们提供了执行原生sql语句的接口,我们可以用orm提供的执行sql语句的接口直接利用sql语句进行查询。

  • 方式1:

    obj = models.Book.objects.raw('select * from app01_book')
    print(obj)
    for ret in obj:
    print(ret)
    print(ret.price)

    # 连表可以
    obj = models.Book.objects.raw('select * from app01_book inner join app01_publish on app01_book.publish_id = app01.publish.id')
    print(obj)
    for ret in obj:
       print(ret.name)
       print(ret.title)
  • 方式2:(推荐的)

    from django.db import connection, connections
    cursor = connection.cursor() # cursor = connections['default'].cursor() cursor.execute("""SELECT * from auth_user where id = %s""", [1])
    ​
    # 这里可以用格式化输出
    ret = cursor.fetchone()

 

查看原生sql的第二种方式

  • 第一种是配置settings

    LOGGING = {
       'version': 1,
       'disable_existing_loggers': False,
       'handlers': {
           'console':{
               'level':'DEBUG',
               'class':'logging.StreamHandler',
          },
      },
       'loggers': {
           'django.db.backends': {
               'handlers': ['console'],
               'propagate': True,
               'level':'DEBUG',
          },
      }
    }
  • 第二种方式可以单独查询某条orm翻译的sql语句。

    ret = models.Book.objects.all()
    print(ret)
    from django.db import connection
    print(connection.queries)

     

  •  

posted @ 2020-06-08 19:21  牧羊小董  阅读(256)  评论(0)    收藏  举报