1. 程式人生 > >django-聚合、分組、F查詢和Q查詢、總結

django-聚合、分組、F查詢和Q查詢、總結

準備測試資料

from django.db import models


# Create your models here.


class Publisher(models.Model):
    id = models.AutoField(primary_key=True)

    name = models.CharField(max_length=32)

    addr = models.CharField(max_length=32)

    phone = models.IntegerField

    def
__str__(self):
return self.name # 作者查書,設計到作者裡面 class Author(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=16) author_detail = models.OneToOneField("AuthorDetail") # 多對多 books = models.ManyToManyField(to="Book") def __str__
(self):
return self.name class Book(models.Model): id = models.AutoField(primary_key=True) title = models.CharField(max_length=6) price = models.DecimalField(max_digits=5, decimal_places=2) publish_day = models.DateField(auto_now_add=True) # 書-出版社 多對一關聯 publisher = models.ForeignKey(to="Publisher"
, to_field="id") def __str__(self): return self.title class AuthorDetail(models.Model): id = models.AutoField(primary_key=True) city = models.CharField(max_length=32) email = models.EmailField() def __str__(self): return self.city

這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

聚合查詢

aggregate()是QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的識別符號,值是計算出來的聚合值。鍵的名稱是按照欄位和聚合函式的名稱自動生成出來的。

from django.db.models import Max,Avg,F,Q
models.Book.objects.all().aggregate(Avg("price"))
{'price__avg': 192.593333}

如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。

models.Book.objects.all().aggregate(avg_price=Avg("price"))
{'avg_price': 192.593333}

如果你希望生成不止一個聚合,你可以向aggregate()子句中新增另一個引數。所以,如果你也想知道所有圖書價格的最大值和最小值,可以這樣查詢:

from django.db.models import Max,Avg,F,Q,Min,Count,Sum
models.Book.objects.all().aggregate(Avg("price"),Max("price"),Min("price"),Sum("price"))
{'price__avg': 192.593333, 'price__max': Decimal('233.33'), 'price__min': Decimal('111.12'), 'price__sum': Decimal('577.78')}

分組查詢

為呼叫的QuerySet中每一個物件都生成一個獨立的統計值
統計每一本書的作者個數

models.Book.objects.all().annotate(authorNum=Count("author"))
<QuerySet [<Book: 書一>, <Book: 書二>, <Book: 書三>]>
book_list = models.Book.objects.all().annotate(authorNum=Count("author"))
for i in book_list:
    print(i.authorNum)
2
2
1

統計出每個出版社買的最便宜的書的價格
方法一

pulisher_list = models.Publisher.objects.all().annotate(min_price=Min("book__price"))
for i in pulisher_list:
    print(i.min_price)
111.12
12.00
456.00
785.00

方法二

tmp = models.Book.objects.all().values("author__name").annotate(min_price=Min("price"))
for i in tmp:
    print(i.min_price)
Traceback (most recent call last):
  File "<input>", line 2, in <module>
AttributeError: 'dict' object has no attribute 'min_price'
for i in tmp:
    print(i)

{'author__name': '小一', 'min_price': Decimal('111.12')}
{'author__name': '小二', 'min_price': Decimal('233.33')}
{'author__name': '小三', 'min_price': Decimal('111.12')}
{'author__name': None, 'min_price': Decimal('12.00')}

統計不止一個作者的圖書

tmp = models.Book.objects.all().annotate(au=Count("author")).filter(au__gt=1)
tmp
<QuerySet [<Book: 書一>, <Book: 書二>]>

根據一本圖書作者數量的多少對查詢集 QuerySet進行排序

tmp = models.Book.objects.all().annotate(au=Count("author")).order_by("au")
tmp
<QuerySet [<Book: 書四>, <Book: 書五>, <Book: 書六>, <Book: 書三>, <Book: 書一>, <Book: 書二>]>

查詢各個作者出的書的總價格

tmp = models.Author.objects.all().annotate(total=Sum("books__price")).values("name","total")
tmp
<QuerySet [{'name': '小一', 'total': Decimal('344.45')}, {'name': '小二', 'total': Decimal('689.33')}, {'name': '小三', 'total': Decimal('111.12')}, {'name': '小四', 'total': None}]>

F查詢

在上面所有的例子中,我們構造的過濾器都只是將欄位值與某個常量做比較。如果我們要對兩個欄位的值做比較,那該怎麼做呢?

Django 提供 F() 來做這樣的比較。F() 的例項可以在查詢中引用欄位,來比較同一個 model 例項中兩個不同欄位的值。

查詢書id大於\小於價格的書籍

models.Book.objects.filter(id__gt=F("price"))
<QuerySet []>

models.Book.objects.filter(id__lt=F("price"))
<QuerySet [<Book: 書一>, <Book: 書二>, <Book: 書三>, <Book: 書四>, <Book: 書五>, <Book: 書六>]>

Django 支援 F() 物件之間以及 F() 物件和常數之間的加減乘除和取模的操作。
這裡寫圖片描述

models.Book.objects.filter(id__lt=F("price")/2)
<QuerySet [<Book: 書一>, <Book: 書二>, <Book: 書三>, <Book: 書四>, <Book: 書五>]>

修改操作也可以使用F函式,比如將每一本書的價格提高30元

models.Book.objects.all().update(price=F("price")+30)

修改之後的截圖如下:
這裡寫圖片描述

Concat連結

from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F("title"),Value("("),Value("第"),Value(")")))

修改之後的表如下:
這裡寫圖片描述

Q查詢

filter() 等方法中的關鍵字引數查詢都是一起進行“AND” 的。 如果你需要執行更復雜的查詢(例如OR語句),你可以使用Q物件

查詢作者名是小仙女或小魔女的

models.Author.objects.filter(Q(name="小一") | Q(name="小三"))
<QuerySet [<Author: 小一>, <Author: 小三>]>

你可以組合& 和| 操作符以及使用括號進行分組來編寫任意複雜的Q 物件。同時,Q 物件可以使用~ 操作符取反,這允許組合正常的查詢和取反(NOT) 查詢

查詢作者名字是小仙女並且不是2018年出版的書的書名。

models.Book.objects.filter(Q(author__name="小一") & ~Q(publish_day__year=2018)).values_list("title")
<QuerySet [('書一',)]>

查詢函式可以混合使用Q 物件和關鍵字引數。所有提供給查詢函式的引數(關鍵字引數或Q 物件)都將”AND”在一起。但是,如果出現Q 物件,它必須位於所有關鍵字引數的前面

models.Book.objects.filter(Q(publish_day__year=2018) | Q(publish_day__year=2017), title__icontains="書")
<QuerySet [<Book: 書一>, <Book: 書二>, <Book: 書三(第)>, <Book: 書四(第)>, <Book: 書五(第)>, <Book: 書六(第)>]>

總結

ORM 跨表查詢
    class Book(models.Model):   
        title = models.CharField( max_length=32)

        publish=models.ForeignKey(to="Publish",to_field="id")
        authors=models.ManyToManyField(to='Author',related_name='bookList') 

    class Publish(models.Model):
        name=models.CharField( max_length=32)


    class Author(models.Model):
        name=models.CharField( max_length=32)
        ad=models.OneToOneField("AuthorDetail")

    class AuthorDetail(models.Model):

        telephone=models.BigIntegerField()


    基於物件查詢(sql:子查詢)


          一對多的關係  (Publish--Book)
                  正向查詢,按欄位:

                  查詢python這本書的出版社所在的名稱
                  book_obj=Book.objects.filter(title="python").first()
                  #print(book_obj.publish.name)

                  反向查詢,按表名小寫_set:

                  人民出版社出版過的所有書籍名稱

                  publish_obj=Publish.objects.filter(name="人民出版社出版").first()
                  print(publish_obj.book_set.all())  
                  for obj in publish_obj.book_set.all():
                       print(obj.title)


          多對多的關係
                正向查詢,按欄位:
                    python這本書所有作者的名字
                        book_obj=Book.objects.filter(title="python").first()
                        book_obj.authors.all()

                反向查詢,按表名小寫_set:
                    alex出版過的所有書籍名稱
                    alex=Author.objects.filter(name="alex").first()
                    alex.bookList.all()

          一對一的關係
                正向查詢,按欄位:
                    查詢alex的手機號
                    alex=Author.objects.filter(name="alex").first()
                    alex.ad.telephone

                反向查詢:按表名小寫
                    以151開頭的手機號的作者的名字
                    ad=AuthorDetail.objects.get(telephone__startswith="151")
                    ad.author.name      


    基於Queryset和 __(sql:join語句):

          正向查詢,按欄位
          反向查詢,按表名小寫

          一對多的關係  (Publish--Book)


                  查詢python這本書的所在出版社的名稱
                  Book.objects.filter(title="python").values("publish__name")
                  for obj in Book.objects.filter(title="python"):
                      temp={}
                      temp["publish__name"]=obj.publish.name

                  人民出版社出版過的所有書籍名稱         
                  Publish.objects.filter(name="人民出版社出版").values("book__title")



          多對多的關係

                    python這本書所有作者的名字
                        Book.objects.filter(title="python").values("authors__name")


                    alex出版過的所有書籍名稱
                        Author.objects.filter(name="alex").values("book__title")


          一對一的關係

                    查詢alex的手機號
                        Author.objects.filter(name="alex").values("ad__telephone")

                    以151開頭的手機號的作者的名字
                    AuthorDetail.objects.filter(telephone__startswith="151").values("author__name")

         擴充套件:
                練習1:
                查詢python這本書的所在出版社的名稱
                Book.objects.filter(title="python").values("publish__name")          
                Publish.objects.filter(book__title="python").values("name")

                練習2:
                手機號以151開頭的作者出版過的所有書籍名稱以及出版社名稱
                Book.objects.filter(authors__ad__telephone__startswith="151").values("title","publish__name")

    分組查詢:
            查詢每一個出版社出版過的書籍個數    
            Publish.objects.annotate(Count("book__id"))
            select count(*) from publish  group by id