咱們一般作查詢操做的時候,都是經過模型名字.objects的方式進行操做。其實模型名字.objects是一個django.db.models.manager.Manager對象,而Manager這個類是一個「空殼」的類,他自己是沒有任何的屬性和方法的。他的方法所有都是經過Python動態添加的方式,從QuerySet類中拷貝過來的。javascript
這個對象是django.db.models.manager.Manager
的對象,這個類是一個空殼類,他上面的全部方法都是從QuerySet
這個類上面拷貝過來的。所以咱們只要學會了QuerySet
,這個objects
也就知道該如何使用了。
Manager
源碼解析:html
class_name = "BaseManagerFromQuerySet" class_dict = { '_queryset_class': QuerySet } class_dict.update(cls._get_queryset_methods(QuerySet)) # type動態的時候建立類 # 第一個參數是用來指定建立的類的名字。建立的類名是:BaseManagerFromQuerySet # 第二個參數是用來指定這個類的父類。 # 第三個參數是用來指定這個類的一些屬性和方法 return type(class_name,(cls,),class_dict) _get_queryset_methods:這個方法就是將QuerySet中的一些方法拷貝出來
將知足條件的數據提取出來,返回一個新的QuerySet。具體的filter能夠提供什麼條件查詢java
排除知足條件的數據,返回一個新的QuerySet。示例代碼以下:python
Article.objects.exclude(title__contains='hello')
以上代碼的意思是提取那些標題不包含hello的圖書。ios
給QuerySet中的每一個對象都添加一個使用查詢表達式(聚合函數、F表達式、Q表達式、Func表達式等)的新字段。示例代碼以下:web
articles = Article.objects.annotate(author_name=F("author__name"))
以上代碼將在每一個對象中都添加一個author__name的字段,用來顯示這個文章的做者的年齡。sql
使用聚合函數。數據庫
# 根據建立的時間正序排序 articles = Article.objects.order_by("create_time") # 根據建立的時間倒序排序 articles = Article.objects.order_by("-create_time") # 根據做者的名字進行排序 articles = Article.objects.order_by("author__name") # 首先根據建立的時間進行排序,若是時間相同,則根據做者的名字進行排序 articles = Article.objects.order_by("create_time",'author__name')
必定要注意的一點是,多個order_by
,會把前面排序的規則給打亂,而使用後面的排序方式。好比如下代碼:django
articles = Article.objects.order_by("create_time").order_by("author__name")
他會根據做者的名字進行排序,而不是使用文章的建立時間。
固然,也能夠在模型定義的在Meta
類中定義ordering
來指定默認的排序方式。示例代碼以下:api
class Meta: db_table = 'book_order' ordering = ['create_time','-price']
還能夠根據annotate
定義的字段進行排序。好比要實現圖書的銷量進行排序,那麼示例代碼以下:
books = Book.objects.annotate(order_nums=Count("bookorder")).order_by("-order_nums") for book in books: print('%s/%s'%(book.name,book.order_nums))
values
的返回值一樣也是一個QuerySet
對象,可是這個QuetySet
中裝的就不是模型了,而是一個一個的dict
字典.
若是咱們想要提取的是這個模型上關聯的對象的屬性,那麼也是能夠的,查找順序跟filter
的用法是同樣的.示例代碼以下:
books = Book.objects.values("id","name","author__name")
以上將會提取author
模型下的name
字段,若是咱們不想要這個名字,想要更改一個名字,那麼可使用關鍵字參數.示例代碼以下:
books = Book.objects.values("id","name",author_name=F("author__name"))
自定義的名字,不能和模型上自己擁有的字段同樣,好比以上author_name
若是取名叫作author
就會報錯,由於Book
上自己就擁有一個字段叫作author
.
在values
中,也可使用聚合函數來造成一個新的字段.好比我想要獲取每本圖書的銷量,那麼示例代碼以下:
books = Book.objects.values("id","name",order_nums=Count("bookorder"))
若是調用values
方法的時候,沒有傳遞任何的參數,那麼會獲取這個模型上的全部字段以及對應的值造成的字典.示例代碼以下:
books = Book.objects.values()
d跟values
是同樣的做用.只不過這個方法返回的QuerySet
中,裝的不是字典,而是元組,示例代碼以下:
books = Book.objects.values_list("id","name")
那麼以上代碼的返回結果是:
(1,"西遊記")
若是給values_list
只指定一個字段,那麼咱們能夠指定flat=True
,這樣返回回來的結果就不在是一個元組,而是這個字段的值,示例代碼以下:
books = Book.objects.values_list("name",flat=True)
那麼以上返回的結果是:
`三國演義`
必定要注意的是,flat只能用在只有一個字段的狀況下,不然就會報錯.
查詢模型下的全部數據,返回一個QuerySet
對象,這個QuerySet
對象沒有通過任何的修改(好比過濾等)
在查找某個表的數據的時候,能夠一次性把相關聯的其餘表的數據都提取出來,這樣能夠在之後訪問相關聯的表的數據的時候,不用再次查找數據庫,能夠節省一些開銷.示例代碼以下:
books = Book.objects.select_related("author","publisher") for book in books: print(book.author.name) # 由於在提取book的時候,使用了select_related,那麼之後在訪問book.author的時候,不會再次向數據庫從新發起查詢
注意:這個方法只能用在外鍵關聯的對象上,對於那種多對多,或者多對一的狀況不能使用它在實現,而應該使用prefetch_related
來實現.
這個方法相似與select_related
方法,也是用來查詢語句的時候,提早將查找的數據提取出來.不過這個方法是用來解決多對多,非外鍵的的狀況.這個方法會產生兩個查詢語句.因此,若是查詢外鍵關聯的模型就用select_related
,若是查詢的是多對多或者非外鍵關聯的狀況,就用prefetch_related
.示例代碼以下:
books = Book.objects.prefetch_related("bookorder_set")
須要注意的是:在使用prefetch_related
查找出來的boororder_set
,建議不要再對它進行任何操做,好比filter
,否則又會產生N多查詢語句,影響查詢的性能.好比如下的代碼是不對的:
books = Book.objects.prefetch_related("bookorder_set") for book in books: print(book.name) # 這個地方若是對bookorder_set進行了操做,那麼就又會產生新的sql語句,前面的prefetch_related就至關於白作了 oredrs = book.bookorder_set.fliter(price__gte=90) for order in orders: print(order.id)
那麼若是確實是想要對預先查找的集合進行操做,那麼咱們可使用django.db.models.Prefetch
來完成,示例代碼以下:
# 先使用Prefetch把查找的條件寫好,在放到prefetch_related中 from django.db.models import Prefetch prefetch = Prefetch("bookorder_set",queryset=Bookorder.objects.filter(price__gte=90)) books = Book.objects.prefetch_related(prefetch) for book in books: print(book.name) orders = book.bookorder_set.all() for order in orders: print(order.id)
這兩個方法都會返回一個QuerySet
,而且這個QuerySet
中裝的都是模型,而不是字段
1. defer
: 這個方法用來告訴ORM
,在查詢某個模型的時候,過濾到某些字段.
2. only
: 這個方法用來告訴ONR
,在查詢某個模型的時候,只提取某些字段.
注意: 使用defer
了的字段,之後在使用這個字段,會從新發起一次請求,所以要謹慎操做,only
也同理.示例代碼以下:
articles = list(Article.objects.defer("title")) for article in articles: # 由於在上面提取的時候過濾了title # 這個地方從新獲取title,將從新向數據庫中進行一次查找操做 print(article.title) for sql in connection.queries: print('='*30) print(sql)
獲取知足條件的數據,返回的是具體的模型。這個函數只能返回一條數據,而且若是給的條件有多條數據,那麼這個方法會拋出MultipleObjectsReturned錯誤,若是給的條件沒有任何數據,那麼就會拋出DoesNotExit錯誤。因此這個方法在獲取數據的只能,只能有且只有一條。
建立一條數據,而且保存到數據庫中。這個方法至關於先用指定的模型建立一個對象,而後再調用這個對象的save方法。示例代碼以下:
article = Article(title='abc') article.save() # 下面這行代碼至關於以上兩行代碼 article = Article.objects.create(title='abc')
一次性建立多個數據,無論多少條數據,一條SQL語句解決
若是給定的條件有數據,那麼就會把這個數據直接提取出來.若是給定的條件沒有數據,那麼就會先建立數據,而後再把數據返回回來.
count
:獲取提取的數據的個數。若是想要知道總共有多少條數據,那麼建議使用count
,而不是使用len(articles)
這種。由於count在底層是使用select count(*)
來實現的,這種方式比使用len函數更加的高效。
返回QuerySet中的第一條和最後一條數據,返回值是數據的模型.
exists
:判斷某個條件的數據是否存在。若是要判斷某個條件的元素是否存在,那麼建議使用exists
,這比使用count
或者直接判斷QuerySet
更有效得多。示例代碼以下:
if Article.objects.filter(title__contains='hello').exists(): print(True) 比使用count更高效: if Article.objects.filter(title__contains='hello').count() > 0: print(True) 也比直接判斷QuerySet更高效: if Article.objects.filter(title__contains='hello'): print(True)
distinct
:去除掉那些重複的數據。這個方法若是底層數據庫用的是MySQL,那麼不能傳遞任何的參數。好比想要提取全部銷售的價格超過80元的圖書,而且刪掉那些重複的,那麼可使用distinct
來幫咱們實現,示例代碼以下:
books = Book.objects.filter(bookorder__price__gte=80).distinct()
須要注意的是,若是在distinct
以前使用了order_by
,那麼由於order_by
會提取order_by
中指定的字段,所以再使用distinct
就會根據多個字段來進行惟一化,因此就不會把那些重複的數據刪掉。示例代碼以下:
orders = BookOrder.objects.order_by("create_time").values("book_id").distinct()
那麼以上代碼由於使用了order_by,即便使用了distinct,也會把重複的book_id提取出來。
一次性能夠把全部的數據都更新完
update
:執行更新操做,在SQL底層走的也是update
命令。好比要將全部category爲空的article的article字段都更新爲默認的分類。示例代碼以下:
Article.objects.filter(category__isnull=True).update(category_id=3)
注意這個方法走的是更新的邏輯。因此更新完成後保存到數據庫中不會執行save
方法,所以不會更新auto_now
設置的字段。
delete
:刪除全部知足條件的數據。刪除數據的時候,要注意on_delete
指定的處理方式。
切片操做:有時候咱們查找數據,有可能只須要其中的一部分。那麼這時候可使用切片操做來幫咱們完成。QuerySet使用切片操做就跟列表使用切片操做是同樣的。示例代碼以下:
books = Book.objects.all()[1:3] for book in books: print(book)
切片操做並非把全部數據從數據庫中提取出來再作切片操做。而是在數據庫層面使用LIMIE和OFFSET來幫咱們完成。因此若是隻須要取其中一部分的數據的時候,建議你們使用切片操做。
生成一個QuerySet對象並不會立刻轉換爲SQL語句去執行。
好比咱們獲取Book表下全部的圖書:
books = Book.objects.all() print(connection.queries)
咱們能夠看到在打印connection.quries的時候打印的是一個空的列表。說明上面的QuerySet並無真正的執行。
在如下狀況下QuerySet會被轉換爲SQL語句執行:
迭代
:在遍歷QuerySet對象的時候,會首先先執行這個SQL語句,而後再把這個結果返回進行迭代。好比如下代碼就會轉換爲SQL語句:for book in Book.objects.all(): print(book)
使用步長
作切片
操做:QuerySet能夠相似於列表同樣作切片操做。作切片操做自己不會執行SQL語句,可是若是若是在作切片操做的時候提供了步長,那麼就會立馬執行SQL語句。須要注意的是,作切片後不能再執行filter方法,不然會報錯。
調用len函數
:調用len函數用來獲取QuerySet中總共有多少條數據也會執行SQL語句。
調用list函數
:調用list函數用來將一個QuerySet對象轉換爲list對象也會立馬執行SQL語句。
判斷
:若是對某個QuerySet進行判斷,也會立馬執行SQL語句。