8、Django的orm之多表操做(二)

1、查詢(重點)sql

  1. 基於對象的跨表查詢 -- 相似於子查詢

正向查詢和反向查詢django

關係屬性(字段)寫在哪一個表裏面,從當前類(表)的數據去查詢它關聯類(表)的數據叫作正向查詢,反之叫作反向查詢app

  • 一對一查詢
    • 正向查詢
      # 1.查詢jiege的地址
      author_obj = models.Author.objects.get(name='jiege')
      print(author_obj.authorDetail.addr)函數

      # 或者用filter這種查,可是要加.first()變成models對象,不然報錯
      author_obj1 = models.Author.objects.filter(name='jiege').first()
      print(author_obj1.authorDetail.addr)
      
      # 正向 author_obj.authorDetail 也就是 對象.關聯屬性名稱
    • 反向查詢
      # 2.查詢 1996-02-14 是誰的生日
      authordetail_obj = models.AuthorDetail.objects.get(birthday='1996-02-14')
      print(authordetail_obj.author.name)工具

      # 反向 authordetail_obj.author  也就是 對象.小寫的另外一個表名(類名)
    • 總結
      Author表 一對一關聯 AuthorDetail表fetch

      正向查詢:Authorobj.authorDetail,對象.關聯屬性名稱
      Author------------------------------------------------->AuthorDetail
           <-------------------------------------------------
             反向查詢:AuthorDetailobj.author  ,對象.小寫類名
  • 一對多的查詢
    注:關係字段在哪一個表,哪一個表就是「多「,被關聯的表是」一「
    • 正向查詢
      # 1.查詢 回村的誘惑 這本書是哪一個出版社出版的
      book_obj = models.Book.objects.get(title='回村的誘惑')
      print(book_obj.publish.name)code

      # 正向 book_obj.publish  也就是 對象名.關聯屬性名稱
    • 反向查詢
      # 2.查詢 外交出版社 都出版了哪些書
      publish_obj = models.Publish.objects.get(name='外交出版社')
      print(publish_obj.book_set.all()) # <QuerySet [<Book: yuhao的回憶錄>, <Book: 傑哥你真猛>]>
      print(publish_obj.book_set.all().filter(price__gt=500)) # <QuerySet [<Book: 傑哥你真猛>]>orm

      # 反向 publish_obj.book_set  也就是 對象.表名小寫_set
      # 由於一個出版社能夠對應不少書,因此用 book_set
      # 由於結果返回一個queryset對象,能夠繼續加 .方法
    • 總結
      Book表 一對多關聯 Publish表對象

      正向查詢:book_obj.publish,對象.關聯屬性名稱
      Book -------------------------------------------------> Publish
           <-------------------------------------------------
             反向查詢:publish_obj.book_set.all(),對象.表名小寫_set
  • 多對多的查詢
    • 正向查詢
      # 1.查詢 yuhao的回憶錄 這本書的做者都有誰
      book_obj = models.Book.objects.get(title='yuhao的回憶錄')
      print(book_obj.authors.all()) # <QuerySet [<Author: jiege>, <Author: yuhao>, <Author: liangdao>]>事務

      # 正向 book_obj.authors.all() 就是 對象.屬性
    • 反向查詢
      # 2.查詢 liangdao 都寫過哪些書
      author_obj = models.Author.objects.get(name='liangdao')
      print(author_obj.book_set.all()) # <QuerySet [<Book: yuhao的回憶錄>, <Book: 裝13是如何煉成的2>]>

      # 反向 book_obj.author_obj.book_set.all()  就是 對象.表名小寫_set
    • 總結
      Book表 多對多關聯 Author表

      正向查詢:book_obj.authors.all(),對象.關聯屬性名稱
      Book -------------------------------------------------> Author
           <-------------------------------------------------
             反向查詢:author_obj.book_set.all(),對象.表名小寫_set
  1. 基於雙下劃線的跨表查詢 -- 相似連表join

正向查詢和反向查詢

  • 一對一
    • 查詢 jiege 的地址
      # 方式1:正向查詢
      obj = models.Author.objects.filter(name='jiege').values('authorDetail__addr')
      print(obj) # <QuerySet [{'authorDetail__addr': '天空之城'}]>

      # 方式2:反向查詢
      obj1 = models.AuthorDetail.objects.filter(author__name='jiege').values('addr')
      print(obj1) # <QuerySet [{'addr': '天空之城'}]>
    • 哪一個做者的生日是 2019 - 07 - 19
      # 方式1:正向查詢
      obj = models.Author.objects.filter(authorDetail__birthday='2019-07-19').values('name')
      print(obj) # <QuerySet [{'name': 'liangge'}]>

      # 方式2:反向查詢
      obj1 = models.AuthorDetail.objects.filter(birthday='2019-07-19').values('author__name')
      print(obj1)  # <QuerySet [{'author__name': 'liangge'}]>
  • 一對多
    • 查詢一下 裝13是如何煉成的 這本書的出版社是哪一個
      # 方式1:正向查詢
      obj = models.Book.objects.filter(title='裝13是如何煉成的').values('publish__name')
      print(obj) # <QuerySet [{'publish__name': '膨脹出版社'}]>

      # 方式2:反向查詢
      obj1 = models.Publish.objects.filter(book__title='裝13是如何煉成的').values('name')
      print(obj1)  # <QuerySet [{'name': '膨脹出版社'}]>
    • 膨脹出版社 出版了哪些書
      # 方式1:正向查詢
      obj = models.Book.objects.filter(publish__name='膨脹出版社').values('title')
      print(obj)
      # <QuerySet [{'title': '裝13是如何煉成的'}, {'title': '回村的誘惑'}, {'title': '裝13是如何煉成的2'}, {'title': '傑哥誘惑'}]>

      # 方式2:反向查詢
      obj1 = models.Publish.objects.filter(name='膨脹出版社').values('book__title')
      print(obj1)
      # <QuerySet [{'book__title': '裝13是如何煉成的'}, {'book__title': '回村的誘惑'}, {'book__title': '裝13是如何煉成的2'}, {'book__title': '傑哥誘惑'}]>
  • 多對多
    • 傑哥誘惑 這本書是誰寫的
      # 方式1:正向查詢
      obj = models.Book.objects.filter(title='傑哥誘惑').values('authors__name')
      print(obj) # <QuerySet [{'authors__name': 'yuhao'}]>

      # 方式2:反向查詢
      obj1 = models.Author.objects.filter(book__title='傑哥誘惑').values('name')
      print(obj1)  # <QuerySet [{'name': 'yuhao'}]>
    • yuhao 都寫了哪些書
      # 方式1:正向查詢
      obj = models.Book.objects.filter(authors__name='yuhao').values('title')
      print(obj)
      # <QuerySet [{'title': '裝13是如何煉成的'}, {'title': 'yuhao的回憶錄'}, {'title': '裝13是如何煉成的2'}, {'title': '傑哥誘惑'}]>

      # 方式2:反向查詢
      obj1 = models.Author.objects.filter(name='yuhao').values('book__title')
      print(obj1)
      # <QuerySet [{'book__title': '裝13是如何煉成的'}, {'book__title': 'yuhao的回憶錄'}, {'book__title': '裝13是如何煉成的2'}, {'book__title': '傑哥誘惑'}]>
  • 進階
    • 裝13出版社 出版的書的名稱以及做者的名字
      # 關聯了三張表,Book、Author、publish

      方式一:
          obj = models.Publish.objects.filter(name='裝13出版社').values('book__title','book__authors__name')
          print(obj)
          # <QuerySet [{'book__title': '回孃家的誘惑', 'book__authors__name': 'jiege'}, {'book__title': '回孃家的誘惑', 'book__authors__name': 'yuhao'}]>
      
      方式二:
          obj1 = models.Book.objects.filter(publish__name='裝13出版社').values('title','authors__name')
          print(obj1)
          # <QuerySet [{'title': '回孃家的誘惑', 'authors__name': 'jiege'}, {'title': '回孃家的誘惑', 'authors__name': 'yuhao'}]>
      
      方式三:
          obj2 = models.Author.objects.filter(book__publish__name='裝13出版社').values('book__title','name')
          print(obj2)
          # <QuerySet [{'book__title': '回孃家的誘惑', 'name': 'jiege'}, {'book__title': '回孃家的誘惑', 'name': 'yuhao'}]>

      原生的sql語句是這樣的:
      SELECT
      app01_book.title,
      app01_author.name
      FROM
      app01_publish
      INNER JOIN app01_book ON app01_publish.nid = app01_book.publish_id
      INNER JOIN app01_book_authors ON app01_book.nid = app01_book_authors.book_id
      INNER JOIN app01_author ON app01_author.nid = app01_book_authors.author_id
      WHERE
      app01_publish.name = '裝13出版社';
      使用Navicat工具:

    • 手機號以4開頭的做者出版過的全部書籍名稱以及出版社名稱
      # 關聯了四張表,Book、Author、publish、AuthorDetail

      # 方式一
          obj = models.AuthorDetail.objects.filter(telephone__startswith='4').values('author__book__title','author__book__publish__name')
          print(obj)
      
      # 方式二
          obj1 = models.Book.objects.filter(authors__authorDetail__telephone__startswith='4').values('title','publish__name')
          print(obj1)
      
      # 方式三    
          obj2 = models.Publish.objects.filter(book__authors__authorDetail__telephone__startswith='4').values('book__title','name')
          print(obj2)
      
      # 方式四
          obj3 = models.Author.objects.filter(authorDetail__telephone__startswith='4').values('book__title','book__publish__name')
          print(obj3)

3.related_name

反向查詢時,若是定義了related_name ,則用related_name替換 表名,

注意:,用在外鍵的建立 ForeignKey 中的一個參數,只會影響反向查詢

例如:

# 在建立Book表的時候

class Book(models.Model):
    ......
    publish=models.ForeignKey(to="Publish", to_field = "nid", on_delete = models.CASCADE,related_name='xx')
    ......
    
# 由於定義了related_name='xx',因此
    # 在正向查詢時,不會影響什麼
    # 在反向查詢時,就不會用小寫的表名了,而是必須用'xx',不然會報錯
    
好比查詢 裝13是如何煉成的 這本書的出版社的名字
正向查詢:
    obj = models.Book.objects.filter(title='裝13是如何煉成的').values('publish__name')
    print(obj)
反向查詢:
    # 沒加related_name='xx'
    obj1 = models.Publish.objects.filter(book__title='裝13是如何煉成的').values('name')
    print(obj1)
    # 加入了related_name='xx'
    obj1 = models.Publish.objects.filter(xx__title='裝13是如何煉成的').values('name')
    print(obj1)

2、聚合查詢

  1. 聚合

計算全部圖書的平均價格、最高價格

from django.db.models import Avg,Max,Min,Count
obj = models.Book.objects.all().aggregate(a=Avg('price'),m=Max('price'))
print(obj)  # {'a': 411.998571, 'm': Decimal('998.00')}

注意點:

  • aggregate()是QuerySet 的一個終止子句,獲得的是個字典
  • 字典經過鍵取值,獲得的是一個數字,可直接用於計算
    print(obj['m'] - 2) #{'price__avg': 2.833333}
  1. 分組

annotate()爲調用的QuerySet中每個對象都生成一個獨立的統計值(統計方法用聚合函數)。

ret = models.Publish.objects.annotate(a=Avg('book__price')).values('a')
print(ret)
# <QuerySet [{'a': 449.2475}, {'a': 188.0}, {'a': 449.5}]>

ret1 = models.Book.objects.values('publish_id').annotate(a=Avg('price'))
print(ret1)
# <QuerySet [{'publish_id': 1, 'a': 449.2475}, {'publish_id': 2, 'a': 188.0}, {'publish_id': 3, 'a': 449.5}]>

ret2 = models.Emp.objects.values('dep_id','name').annotate(a=Count(1))
# 這裏若是你寫了其餘字段,那麼只有這兩個字段重複,纔算一組,合併到一塊兒來統計個數

注意點:

  • annotate裏面必須寫個聚合函數,否則沒有意義,
  • 而且必須有個別名=,別名隨便寫,可是必須有,
  • 用哪一個字段分組,values裏面就寫哪一個字段,
  • annotate其實就是對分組結果的統計,統計你須要什麼。

6、F查詢、Q查詢

  1. F查詢

針對本身單表中字段的比較和處理,有三種功能

好比在Book表中新建兩個字段,一個收藏數(keep_num),一個評論數(comment_num)

須要先引入:from django.db.models import F

  1. 本身單表中字段的比較
    # 查詢Book本身這張單表中 收藏數 大於 評論數 的書
    ret = models.Book.objects.filter(keep_num__gt=F('comment_num'))
    print(ret)
    # <QuerySet [<Book: 裝13是如何煉成的>, <Book: yuhao的回憶錄>, <Book: 裝13是如何煉成的2>]>

  2. F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操做
    # 查詢Book表中 收藏數 大於 評論數的2倍 的書
    ret = models.Book.objects.filter(keep_num__gt=F('comment_num')*2)
    print(ret)
    # <QuerySet [<Book: 裝13是如何煉成的>, <Book: 裝13是如何煉成的2>]>

  3. 修改、批量的修改操做也可使用F函數
    # 給全部書的價格都減小20
    ret = models.Book.objects.all().update(price=F('price')-20)
    print(ret)
    .

  4. Q查詢

由於查詢的時候,.filter()等方法中的關鍵字查詢都是 and 的關係,表示不了 or 的關係 ,此時, Q查詢就會派上用場

Q 對象可使用&(與) 、|(或)、~(非) 操做符組合起來。

當一個操做符在兩個 Q對象 上使用時,它產生一個新的 Q對象。

先引入:from django.db.models import Q

  1. 使用&、|、~
    ret = models.Book.objects.filter(Q(title='裝13是如何煉成的')|Q(price__lt=100))
    print(ret)

    # 等同於:
    where title='裝13是如何煉成的' or price<100;
  2. 優先級問題(非>與>或),嵌套可解決
    # 此時由於and的優先級高於or,因此先找價格小於100而且評論數大於100的 或者出版日期的年份是2019的書
    ret = models.Book.objects.filter(Q(publishDate__year=2019)|Q(price__lt=100)&Q(comment_num__gt=100))
    print(ret)

    # 由於加入了Q()嵌套,因此是查詢出版日期的年份是2019或者價格小於100的 而且 評論數大於100的書
    ret = models.Book.objects.filter(Q(Q(publishDate__year=2019)|Q(price__lt=100))&Q(comment_num__gt=100))
  3. 混合使用 Q 對象和關鍵字參數
    全部提供給查詢函數的參數(關鍵字參數或Q 對象)都將"AND」在一塊兒。可是,若是出現Q 對象,它必須位於全部關鍵字參數的前面。
    ret = models.Book.objects.filter(Q(publishDate__year=2019)&Q(price__lt=100),title__icontains='yuhao')
    print(ret)

  4. 綜合練習

  5. 查詢每一個做者的姓名以及出版的書的最高價格
    ret = models.Author.objects.annotate(m=Max('book__price')).values('name','m')
    print(ret)

  6. 查詢做者id大於2做者的姓名以及出版的書的平均價格
    ret = models.Author.objects.filter(nid__gt=2).annotate(a=Avg('book__price')).values('name','a')
    print(ret)

  7. 查詢做者id大於2或者做者年齡大於等於20歲的女做者的姓名以及出版的書的最高價格
    ret = models.Author.objects.filter(Q(nid__gt=2)|Q(age__gte=20),sex='female').annotate(m=Max('book__price')).values('name','m')
    print(ret)

  8. 查詢每一個做者出版的書的最高價格 的平均值
    ret = models.Author.objects.annotate(m=Max('book__price')).values('m').aggregate(b=Avg('m'))
    print(ret)

  9. 每一個做者出版的全部書的價格以及最高價格的那本書的名稱
    ret = models.Author.objects.annotate(m=Max('book__price')).values('book__price','book__title')
    print(ret)
    # orm沒法解決這個問題,此時查出來的數據不正確(由於書的名字分組只取到了第一個,而不是最大的那個名字)

    在原生sql中:
    # sql_mode = only_full_group_by  設置這個模式
    select * from (SELECT app01_author.id,app01_author.name,max(app01_book.price) as m 
                   FROM app01_author 
                   INNER JOIN app01_book_authors on app01_author.id=app01_book_authors.author_id
                   INNER JOIN app01_book on app01_book_authors.book_id = app01_book.nid
                   GROUP BY app01_author.id,app01_author.name) as t1 
               INNER JOIN app01_book_authors on t1.id=app01_book_authors.author_id 
               INNER JOIN app01_book on app01_book.nid=app01_book_authors.book_id where                    t1.m=app01_book.price;
    
    # sql_mode != onlu_full_group_by
    SELECT app01_author.id,app01_book.title,app01_book.price 
    FROM 
    app01_author INNER JOIN app01_book_authors on app01_author.id=app01_book_authors.author_id
    INNER JOIN app01_book on app01_book_authors.book_id = app01_book.nid 
    ORDER BY app01_book.price desc
  10. orm執行原生sql語句(瞭解)

Django 提供兩種方法使用原始SQL進行查詢:一種是使用raw()方法,進行原始SQL查詢並返回模型實例;另外一種是徹底避開模型層,直接執行自定義的SQL語句。

  • 執行原生查詢
    注:raw()語法查詢必須包含主鍵
    # 方式一:
    models.Publish.objects.raw('原生sql')
    models.Publish.objects.raw('select * from app01_pubblish')

    # 方式二:
        from django.db import connection
        cursor = connection.cursor()
        cursor.excute('原生sql',[1,])
        cursor.fetchall()
  • 展現sql的
    models.Book.objects.filter(good__gt=F('comment')*2)
    from django.db import connection
    print(connection.queries)

事務四大特性:一致性、持久性、隔離性、原子性

提交事務:start--執行修改---submit提交,rollback回滾取消

相關文章
相關標籤/搜索