1. 程式人生 > 實用技巧 >Django的queryset惰性機制:就是不呼叫不執行,懶唄

Django的queryset惰性機制:就是不呼叫不執行,懶唄


#
查詢相關API: # <1>filter(**kwargs): 它包含了與所給篩選條件相匹配的物件 # <2>all(): 查詢所有結果 # <3>get(**kwargs): 返回與所給篩選條件相匹配的物件,返回結果有且只有一個,如果符合篩選條件的物件超過一個或者沒有都會丟擲錯誤。 #-----------下面的方法都是對查詢的結果再進行處理:比如 objects.filter.values()-------- # <4>values(*field): 返回一個ValueQuerySet——一個特殊的QuerySet,執行後得到的並不是一系列 model的例項化物件,而是一個可迭代的字典序列
# <5>exclude(**kwargs): 它包含了與所給篩選條件不匹配的物件 # <6>order_by(*field): 對查詢結果排序 # <7>reverse(): 對查詢結果反向排序 # <8>distinct(): 從返回結果中剔除重複紀錄 # <9>values_list(*field): 它與values()非常相似,它返回的是一個元組序列,values返回的是一個字典序列
# <10>count(): 返回資料庫中匹配查詢(QuerySet)的物件數量。 # <11>first(): 返回第一條記錄 # <12>last(): 返回最後一條記錄 # <13>exists(): 如果QuerySet包含資料,就返回True,否則返回False。
  #擴充套件查詢,有時候DJANGO的查詢API不能方便的設定查詢條件,提供了另外的擴充套件查詢方法extra:
  #extra(select=None, where=None, params=None, tables=None,order_by=None, select_params=None

---------->惰性機制:

所謂惰性機制:Publisher.objects.all()或者.filter()等都只是返回了一個QuerySet(查詢結果集物件),它並不會馬上執行sql,而是當呼叫QuerySet的時候才執行。

QuerySet特點:

<1> 可迭代的

<2> 可切片

<1>Django的queryset是惰性的

     Django的queryset對應於資料庫的若干記錄(row),通過可選的查詢來過濾。例如,下面的程式碼會得
     到資料庫中名字為‘Dave’的所有的人:person_set = Person.objects.filter(first_name="Dave")
     上面的程式碼並沒有執行任何的資料庫查詢。你可以使用person_set,給它加上一些過濾條件,或者將它傳給某個函式,
     這些操作都不會發送給資料庫。這是對的,因為資料庫查詢是顯著影響web應用效能的因素之一。

<2>要真正從資料庫獲得資料,你可以遍歷queryset或者使用if queryset,總之你用到資料時就會執行sql.
   為了驗證這些,需要在settings里加入 LOGGING(驗證方式)
        obj=models.Book.objects.filter(id=3)
        # for i in obj:
        #     print(i)

        # if obj:
        #     print("ok")

<3>queryset是具有cache的
     當你遍歷queryset時,所有匹配的記錄會從資料庫獲取,然後轉換成Django的model。這被稱為執行
    (evaluation).這些model會儲存在queryset內建的cache中,這樣如果你再次遍歷這個queryset,
     你不需要重複執行通用的查詢。
        obj=models.Book.objects.filter(id=3)

        # for i in obj:
        #     print(i)
                          ## models.Book.objects.filter(id=3).update(title="GO")
                          ## obj_new=models.Book.objects.filter(id=3)
        # for i in obj:
        #     print(i)   #LOGGING只會列印一次

<4>
     簡單的使用if語句進行判斷也會完全執行整個queryset並且把資料放入cache,雖然你並不需要這些
     資料!為了避免這個,可以用exists()方法來檢查是否有資料:

            obj = Book.objects.filter(id=4)
            #  exists()的檢查可以避免資料放入queryset的cache。
            if obj.exists():
                print("hello world!")

<5>當queryset非常巨大時,cache會成為問題

     處理成千上萬的記錄時,將它們一次裝入記憶體是很浪費的。更糟糕的是,巨大的queryset可能會鎖住系統
     程序,讓你的程式瀕臨崩潰。要避免在遍歷資料的同時產生queryset cache,可以使用iterator()方法
     來獲取資料,處理完資料就將其丟棄。
        objs = Book.objects.all().iterator()
        # iterator()可以一次只從資料庫獲取少量資料,這樣可以節省記憶體
        for obj in objs:
            print(obj.name)
        #BUT,再次遍歷沒有列印,因為迭代器已經在上一次遍歷(next)到最後一次了,沒得遍歷了
        for obj in objs:
            print(obj.name)

     #當然,使用iterator()方法來防止生成cache,意味著遍歷同一個queryset時會重複執行查詢。所以使
     #用iterator()的時候要當心,確保你的程式碼在操作一個大的queryset時沒有重複執行查詢

總結:
    queryset的cache是用於減少程式對資料庫的查詢,在通常的使用下會保證只有在需要的時候才會查詢資料庫。
使用exists()和iterator()方法可以優化程式對記憶體的使用。不過,由於它們並不會生成queryset cache,可能
會造成額外的資料庫查詢。