ORM多表查詢下

Bound_w發表於2018-12-20

  

一、多表查詢

1、基於雙下劃線的跨表查詢

       Django 還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關係,它能自動確認 SQL JOIN 聯絡。要做跨關係查詢,就使用兩個下劃線來連結模型(model)間關聯欄位的名稱,直到最終連結到你想要的model 為止。

       語法:正向查詢按欄位,反向查詢按表名小寫,用來告訴ORM引擎join哪張表。

       a、一對多查詢

    示例一:查詢蘋果出版社出版過的所有書籍的名字與價格

    # 正向查詢 按欄位:publish

      queryResult=Book.objects.filter(publish__name="蘋果出版社").values_list("title","price")

    # 反向查詢 按表名小寫:book

        queryResult=Publish.objects.filter(name="蘋果出版社").values_list("book__title","book__price"

        示例二:查python這本書的出版社的名字和郵箱

    # 正向查詢

    Book.objects.filter(title="python").values("publish__name", "publish__email")

    # 反向查詢

    Publish.objects.filter(book__title="python").values("name", "email")

  b、多對多查詢

    示例一:查詢alex出過的所有書籍的名字

    # 正向查詢 按欄位:authors:

        queryResult=Book.objects.filter(authors__name="yuan").values_list("title")

    # 反向查詢 按表名小寫:book

        queryResult=Author.objects.filter(name="yuan").values_list("book__title","book__price")

        示例二:查詢python這本書的作者的年齡

    # 正向查詢

    Book.objects.filter(title="python").values("author__age")

    # 反向查詢

    Author.objects.filter(book__title="python").values("age")

  c、一對一查詢

    示例一:查詢alex的手機號

    # 正向查詢 按欄位:authorDetail

      ret=Author.objects.filter(name="alex").values("authorDetail__telephone")

    # 反向查詢 按表名小寫:author

        ret=AuthorDetail.objects.filter(author__name="alex").values("telephone")

        示例二:查詢手機號為133的作者的名字

    # 正向查詢

    Author.objects.filter(authorDetail__telephone__startswith="133").values("name")

    # 反向查詢

    AuthorDetail.objects.filter(telephone__startswith="133").values("author__name")

  d、進階練習(連續跨表)

              示例一:查詢人民出版社出版過的所有書籍的名字以及作者的姓名

      # 正向查詢

        queryResult=Book.objects.filter(publish__name="人民出版社").values_list("title","authors__name")

      # 反向查詢

        queryResult=Publish.objects.filter(name="人民出版社")
            .values_list("book__title","book__authors__age","book__authors__name")

              示例二:手機號以151開頭的作者出版過的所有書籍名稱以及出版社名稱

      # 方式1:

        queryResult=Book.objects.filter(authors__authorDetail__telephone__startswith="151")
            .values_list("title","publish__name")

      # 方式2:

        ret=Author.objects.filter(authorDetail__telephone__startswith="151")
                    .values("book__title","book__publish__name")

       e、related_name相關

              反向查詢時,如果定義了related_name ,則用related_name的值替換表名,例如:

        publish = ForeignKey(Blog, related_name='bookList')

              示例一:查詢人民出版社出版過的所有書籍的名字與價格(一對多)

              # 反向查詢 不再按表名:book,而是related_name的值bookList

        queryResult=Publish.objects.filter(name="人民出版社").values_list("bookList__title","bookList__price")

2、聚合查詢

       語法:aggregate(*args, **kwargs)

       示例一:計算所有圖書的平均價格

    from django.db.models import Avg    # 首先匯入聚合函式Avg
    ret = Book.objects.all().aggregate(Avg('price'))
    print(ret)  # 結果為:{'price__avg': 34.35}

       aggregate()是QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的識別符號,值是計算出來的聚合值。鍵的名稱是按照欄位和聚合函式的名稱自動生成出來的。如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。並且all()方法也可以不寫,預設查詢所有,如下示例:

    ret = Book.objects.aggregate(average_price=Avg('price'))
    print(ret)  # 結果為:{'average_price': 34.35}

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

    from django.db.models import Avg, Max, Min  # 匯入聚合函式
    ret = Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
    print(ret)
    # 結果為:{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

3、分組查詢

       a、單表分組查詢

              示例一:查詢每一個部門名稱以及對應的員工數,員工表如下:

   emp表:
            id  name  age   salary    dep
            1   alex  12    2000     銷售部
            2   egon  22    3000     人事部
            3   wen   22    5000     人事部
    SQL語句:select dep,Count(*) from emp group by dep;
    ORM:emp.objects.values("dep").annotate(c=Count("id")

       b、多表分組查詢

              示例一:查詢每一個部門名稱以及對應的員工數,表如下:     

    emp表:
            id  name  age   salary   dep_id
            1   alex   12   2000       1
            2   egon  22   3000       2
            3   wen   22   5000       2

    dep表:
            id   name 
            1    銷售部
            2    人事部

    emp-dep:
            id  name  age   salary   dep_id   id   name 
            1   alex   12   2000       1      1    銷售部
            2   egon  22   3000       2      2    人事部
            3   wen   22   5000       2      2    人事部
    SQL語句:select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.id;
    ORM:dep.objetcs.values("id").annotate(c=Count("emp")).values("name","c")

              總結:

                     1)annotate()為呼叫的QuerySet中每一個物件都生成一個獨立的統計值(統計方法用聚合函式)。

                     2)跨表分組查詢本質就是將關聯表join成一張表,再按單表的思路進行分組查詢。

       c、查詢練習

              示例一:統計每一個出版社的最便宜的書

    publishList = Publish.objects.annotate(MinPrice=Min("book__price"))
    for publish_obj in publishList:
        print(publish_obj.name,publish_obj.MinPrice)

      annotate的返回值是querySet,如果不想遍歷物件,可以用上value_list()方法,如下:

    queryResult = Publish.objects.annotate(MinPrice=Min("book__price")).values_list("name","MinPrice")
     print(queryResult)

              示例二:統計每一本書的作者個數

   ret = Book.objects.annotate(authorsNum=Count('authors__name'))

              示例三:統計每一本以py開頭的書籍的作者個數

    queryResult = Book.objects.filter(title__startswith="Py").annotate(num_authors=Count('authors'))

              示例四:統計不止一個作者的圖書

    queryResult = Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

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

    queryResult = Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

              示例六:查詢各個作者出的書的總價格

    # 按author表的所有欄位 group by
    queryResult = Author.objects.annotate(SumPrice=Sum("book__price")).values_list("name","SumPrice")
     print(queryResult)

4、F查詢

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

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

       示例一:查詢評論數大於收藏數的書籍

    from django.db.models import F   # 匯入F函式
    Book.objects.filter(commnetNum__lt=F('keepNum'))

  Django 支援 F() 物件之間以及 F() 物件和常數之間的加減乘除和取模的操作,如下:

       示例二:查詢評論數大於收藏數2倍的書籍

    Book.objects.filter(commnetNum__lt=F('keepNum')*2)

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

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

5、Q查詢

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

    from django.db.models import Q   # 匯入Q函式
    Q(title__startswith='Py')    # 將要關鍵字查詢條件放入Q方法中

  Q 物件可以使用&和|操作符組合起來。當一個操作符在兩個Q物件上使用時,它產生一個新的Q 物件。

       示例一:

    bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))
    # 上面等同於下面的SQL WHERE子句:
    WHERE name ="yuan" OR name ="egon"

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

    booklist = Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")

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

    booklist = Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),title__icontains="python")

二、靜態檔案的設定(測試環境按照如下配置,記住步驟照做即可)

    1)在專案目錄下建立static資料夾;

    2)專案中用到的靜態資源如圖片,js檔案,bootstrap檔案等放到static中(static中再分別建立img、js、bootstrap、css資料夾用於存放這些檔案);

    3)在專案的settings.py檔案中,STATIC_URL = "/static/" 下邊加上如下程式碼:

    STATICFILES_DIRS = [
        os.join.path(BASE_DIR, "static")
    ]

相關文章