一、queryset介紹
在ORM模型中我們查詢到的數據類型中講到queryset數據類型,本節將對其的主要特性進行講解介紹。如bookList=Book.objects.all()查詢到的bookList即為queryset對象。具體介紹如下:
1、支持切片
queryset對象支持且切片操作 ,但是不支持負數形式的切片。
print(bookList[1:])
2、支持遍歷
for book in bookList: print(book.tittle)
3、屬於惰性查詢
查詢集 是惰性執行的 —— 創建查詢集不會帶來任何數據庫的訪問,即bookList=Book.objects.all()並不會執行查詢的相關sql語句,只有在用到數據庫中的數據時才會進行數據庫查詢,如遍歷,切片查詢等。
4、緩存機制
每個查詢集都包含一個緩存來最小化對數據庫的訪問。如下實例:兩次循環打印的結果一樣,且只查詢一次數據庫,中間新插入的數據並不會被查詢到,便是典型的緩存機制,解決辦法就是重新再建一個查詢集。
bookList=Book.objects.all() for book in bookList: print(book.tittle) Book.objects.create(tittle="linux",......) for book in bookList: print(book.tittle)
在一個新創建的查詢集中,緩存為空。首次對查詢集進行求值 —— 同時發生數據庫查詢 ——Django 將保存查詢的結果到查詢集的緩存中並返回明確請求的結果,下來對該查詢集 的再次求值將重用緩存的結果,如上例所示。一定要注意的是緩存是發生在同一個緩存集,若對查詢集使用不當,差生不同的查詢集,如下實例,實際兩次差生不同的兩個查詢集,即對相同的數據查詢執行兩次,顯然會增加你的數據庫負擔。
print([book.title for book in models.Book.objects.all()]) print([book.publishDate for book in models.Book.objects.all()])
5、優化緩存
緩存機制顯然對於數據量很大的數據庫的查詢顯然不是很合理,非常占用內存,所以對一些不需要一次性得到所有數據的操作可以使用相應的優化緩存機制處理,如下:
(1)exist()
如下下例,即使使用if語句進行判斷,也會完全執行整個數據庫查詢,即使我們不需要所有的數據:
bookList=Book.objects.all() if bookList: print("有數據")
但是通過如下方式使用exist()來檢查是否有數據,其實其不會將所有的數據查出來緩存,只會取一個值:
bookList=Book.objects.all().exist() if bookList: print("有數據")
(2)iterator()
當queryset非常巨大時,處理成千上萬的記錄時,將它們一次裝入內存是很浪費的。更糟糕的是,巨大的queryset可能會鎖住系統 進程,讓你的程序瀕臨崩潰。要避免在遍歷數據的同時產生queryset cache,可以使用iterator()方法 來獲取數據,處理完數據就將其丟棄。
bookList=Book.objects.all().iterator() for book in bookList: print(book.tittle)
注意,此時的bookList是一個生成器,取一個刪除一個,所以若再次對其循環取值時,是沒有數據可以取到的。
二、中介模型
class Class(models.Model): #班級表 nid=models.IntegerField(primary_key=True) name=models.CharField(max_length=32) class Student(models.Model): #學生表 nid=models.IntegerField(primary_key=True) name=models.CharField(max_length=12) Class=models.ForeignKey("Class") #與班級表建立多對一關系 courses=models.ManyToManyField("Course",through="Student2Course") #通過中介模型建立學生與課程多對多關系 class Course(models.Model): #課程表 nid=models.IntegerField(primary_key=True) name=models.CharField(max_length=32) class Student2Course(models.Model): #多對多關系表 nid=models.IntegerField(primary_key=True) course=models.ForeignKey("Course") student=models.ForeignKey("Student") score=models.IntegerField()
如上建立的models表類,一個學生有多們課程,一個課程可以有多個學生選,所以二者屬於多對多關系,在之前篇章models模型中我們介紹過,這種關系表並不需要我們自己建立,通過指定多對多關系后,django會幫我們建立包含兩張表主鍵字段的關系表。但是當我們要求關系表不但只有二者的對應關系信息時,如上例中還需要有每門功課的成績的時,這種關系表的建立我們就需要通過中介模型來完成。
如上例,通過through指定關系表Student2Course,然后關系表通過如上方式,我們自己進行建立,這便稱為中介模型。既然關系表還包含其他字段,所以與普通的多對多字段不同,你不能使用add、 create和賦值語句(比如,beatles.members = [...])來創建關系。創建關系還是需要通過查詢或創建models對象后,然后在關系表中創建關系。值得注意的是:remove()方法被禁用也是出於同樣的原因。但是clear() 方法卻是可用的。它可以清空某個實例所有的多對多關系。
三、extra()函數
有些情況下,Django的查詢語法難以簡單的表達復雜的 WHERE 子句,對於這種情況, Django 提供了 extra() QuerySet修改機制 — 它能在 QuerySet生成的SQL從句中注入新子句,此處主要介紹一下它的select參數。
select 參數可以讓你在 SELECT 從句中添加其他字段信息,它應該是一個字典,存放着屬性名到 SQL 從句的映射。簡單點說extra與annotate分組函數的效果相似,都是給查詢到的queryset增加新的查詢結果字段,只是annotate是通過聚合函數對原有數據進行計算得到的結果,而extra則是通過where判斷對原始數據進行判斷得到結果,見如下實例:
annotate():
通過annotate對查詢到queryset進行分組,注意分組的標准是:查詢到每一條數據,以包含的所有字段為標准,若有一個對應字段不一樣,則此條數據便獨自為一組。如思路二查詢的queryset對象只有publish_id字段,故分組時就以其分組了。相同的publish_id自然分到一組,通過count函數也就計算到它的書籍個數。
例:每一個出版社的名字與出版社的書籍的個數
思路一:
ret=Publish.objects.all().annotate(c=Count("book__id")).values("name","c") print(ret)
思路二:
ret=Book.objects.all().values("publish_id").annotate(c=Count("nid")).values("publish__name","c") print(ret)
extra()
如下為extra()函數中select參數應用實例,其結果就是對查詢到的結果增加一個出版年月的字段,並通過values取到此字段。提醒:Book表中publishDate是以秒的形式存儲的,當我們需要統計某一年或者某一年和月出版的書籍數量,我們可以通過此形式"strftime('%%Y-%%m',publishDate)"將其轉換相應格式,其中strftime中第一個參數就是我們想要的時間格式。
ret=Book.objects.all().extra(select={"pub_year_month":"strftime('%%Y-%%m',publishDate)"}).values("title","pub_year_month") print(ret)
四、整體插入
假如需要我們一次性向Book表插入100條數據,我們該怎樣做呢?也許我們會按照如下的方式進行(假設Book表就tittle和price兩個字段):
for i in range(100): Book.objects.create(title="book"+str(i),price=i*4)
上述這樣的方式是可以達到我們一次性插入多條數據,但是顯然不合理,每插入一條數據則會調用一次數據庫。因此我們可以通過bulk_create一次性插入這一百條數據,稱為批量插入,它只會調用一次數據庫,很好的解決了上述的問題,具體實例如下:
bookList=[] for i in range(100): book=Book(title="book"+str(i),price=i*4) bookList.append(book) Book.objects.bulk_create(bookList)
如上示例,我們可以看出bulk_create批量插入的數據必須是實例對象,且多個實例對象需要放在列表中。