本節將詳細介紹查詢集的API,它創建在下面的模型基礎上,與上一節的模型相同:python
from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def __str__(self): # __unicode__ on Python 2 return self.name class Author(models.Model): name = models.CharField(max_length=200) email = models.EmailField() def __str__(self): # __unicode__ on Python 2 return self.name class Entry(models.Model): blog = models.ForeignKey(Blog, on_delete=models.CASCADE) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateField() mod_date = models.DateField() authors = models.ManyToManyField(Author) n_comments = models.IntegerField() n_pingbacks = models.IntegerField() rating = models.IntegerField() def __str__(self): # __unicode__ on Python 2 return self.headline
在內部,建立、過濾、切片和傳遞一個QuerySet不會真實操做數據庫,在你對查詢集提交以前,不會發生任何實際的數據庫操做。mysql
可使用下列方法對QuerySet提交查詢操做:sql
QuerySet是可迭代的,在首次迭代查詢集時執行實際的數據庫查詢。 例如, 下面的語句會將數據庫中全部Entry的headline打印出來:數據庫
for e in Entry.objects.all(): print(e.headline)
切片:若是使用切片的」step「參數,Django 將執行數據庫查詢並返回一個列表。express
Pickling/緩存django
repr()後端
len():當你對QuerySet調用len()時, 將提交數據庫操做。api
list():對QuerySet調用list()將強制提交操做entry_list = list(Entry.objects.all())
緩存
bool()oracle
測試布爾值,像這樣:
if Entry.objects.filter(headline="Test"): print("There is at least one Entry with the headline Test")
注:若是你須要知道是否存在至少一條記錄(而不須要真實的對象),使用exists() 將更加高效。
下面是對於QuerySet的正式定義:
class QuerySet(model=None, query=None, using=None)[source]
QuerySet類具備兩個公有屬性用於內省:
ordered:若是QuerySet是排好序的則爲True,不然爲False。
db:若是如今執行,則返回使用的數據庫。
如下的方法都將返回一個新的QuerySets。重點是加粗的幾個API,其它的使用場景不多。
方法名 | 解釋 |
---|---|
filter() | 過濾查詢對象。 |
exclude() | 排除知足條件的對象 |
annotate() | 使用聚合函數 |
order_by() | 對查詢集進行排序 |
reverse() | 反向排序 |
distinct() | 對查詢集去重 |
values() | 返回包含對象具體值的字典的QuerySet |
values_list() | 與values()相似,只是返回的是元組而不是字典。 |
dates() | 根據日期獲取查詢集 |
datetimes() | 根據時間獲取查詢集 |
none() | 建立空的查詢集 |
all() | 獲取全部的對象 |
union() | 並集 |
intersection() | 交集 |
difference() | 差集 |
select_related() | 附帶查詢關聯對象 |
prefetch_related() |
預先查詢 |
extra() | 附加SQL查詢 |
defer() | 不加載指定字段 |
only() | 只加載指定的字段 |
using() | 選擇數據庫 |
select_for_update() |
鎖住選擇的對象,直到事務結束。 |
raw() | 接收一個原始的SQL查詢 |
filter(**kwargs)
返回知足查詢參數的對象集合。
查找的參數(**kwargs)應該知足下文字段查找中的格式。多個參數之間是和AND的關係。
exclude(**kwargs)
返回一個新的QuerySet,它包含不知足給定的查找參數的對象。
查找的參數(**kwargs)應該知足下文字段查找中的格式。多個參數經過AND鏈接,而後全部的內容放入NOT() 中。
下面的示例排除全部pub_date
晚於2005-1-3且headline爲「Hello」 的記錄:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
下面的示例排除全部pub_date
晚於2005-1-3或者headline 爲「Hello」 的記錄:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')
annotate(*args, **kwargs)
使用提供的聚合表達式查詢對象。
表達式能夠是簡單的值、對模型(或任何關聯模型)上的字段的引用或者聚合表達式(平均值、總和等)。
annotate()的每一個參數都是一個annotation,它將添加到返回的QuerySet每一個對象中。
關鍵字參數指定的Annotation將使用關鍵字做爲Annotation 的別名。 匿名參數的別名將基於聚合函數的名稱和模型的字段生成。 只有引用單個字段的聚合表達式纔可使用匿名參數。 其它全部形式都必須用關鍵字參數。
例如,若是正在操做一個Blog列表,你可能想知道每一個Blog有多少Entry:
>>> from django.db.models import Count >>> q = Blog.objects.annotate(Count('entry')) # The name of the first blog >>> q[0].name 'Blogasaurus' # The number of entries on the first blog >>> q[0].entry__count 42
Blog模型自己沒有定義entry__count
屬性,可是經過使用一個關鍵字參數來指定聚合函數,能夠控制Annotation的名稱:
>>> q = Blog.objects.annotate(number_of_entries=Count('entry')) # The number of entries on the first blog, using the name provided >>> q[0].number_of_entries 42
order_by(*fields)
默認狀況下,根據模型的Meta類中的ordering屬性對QuerySet中的對象進行排序
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')
上面的結果將按照pub_date
降序排序,而後再按照headline升序排序。"-pub_date"前面的負號表示降序順序。 升序是默認的。 要隨機排序,使用"?",以下所示:
Entry.objects.order_by('?')
注:order_by('?')
可能耗費資源且很慢,這取決於使用的數據庫。
若要按照另一個模型中的字段排序,可使用查詢關聯模型的語法。即經過字段的名稱後面跟兩個下劃線(__
),再加上新模型中的字段的名稱,直到但願鏈接的模型。 像這樣:
Entry.objects.order_by('blog__name', 'headline')
若是排序的字段與另一個模型關聯,Django將使用關聯的模型的默認排序,或者若是沒有指定Meta.ordering將經過關聯的模型的主鍵排序。 例如,由於Blog模型沒有指定默認的排序:
Entry.objects.order_by('blog')
與如下相同:
Entry.objects.order_by('blog__id')
若是Blog設置了ordering = ['name']
,那麼第一個QuerySet將等同於:
Entry.objects.order_by('blog__name')
還能夠經過調用表達式的desc()或者asc()方法:
Entry.objects.order_by(Coalesce('summary', 'headline').desc())
考慮下面的狀況,指定一個多值字段來排序(例如,一個ManyToManyField 字段或者ForeignKey 字段的反向關聯):
class Event(Model): parent = models.ForeignKey( 'self', on_delete=models.CASCADE, related_name='children', ) date = models.DateField() Event.objects.order_by('children__date')
在這裏,每一個Event可能有多個排序數據;具備多個children的每一個Event將被屢次返回到order_by()
建立的新的QuerySet中。 換句話說,用order_by()
方法對QuerySet對象進行操做會返回一個擴大版的新QuerySet對象。所以,使用多值字段對結果進行排序時要格外當心。
沒有方法指定排序是否考慮大小寫。 對於大小寫的敏感性,Django將根據數據庫中的排序方式排序結果。
能夠經過Lower將一個字段轉換爲小寫來排序,它將達到大小寫一致的排序:
Entry.objects.order_by(Lower('headline').desc())
能夠經過檢查QuerySet.ordered
屬性來知道查詢是不是排序的。
每一個order_by()
都將清除前面的任何排序。 例以下面的查詢將按照pub_date
排序,而不是headline:
Entry.objects.order_by('headline').order_by('pub_date')
reverse()
反向排序QuerySet中返回的元素。 第二次調用reverse()將恢復到原有的排序。
如要獲取QuerySet中最後五個元素,能夠這樣作:
my_queryset.reverse()[:5]
這與Python直接使用負索引有點不同。 Django不支持負索引,只能曲線救國。
distinct(*fields)
去除查詢結果中重複的行。
默認狀況下,QuerySet不會去除重複的行。當查詢跨越多張表的數據時,QuerySet可能獲得重複的結果,這時候可使用distinct()進行去重。
values(*fields, **expressions)
返回一個包含數據的字典的queryset,而不是模型實例。
每一個字典表示一個對象,鍵對應於模型對象的屬性名稱。
下面的例子將values() 與普通的模型對象進行比較:
# 列表中包含的是Blog對象 >>> Blog.objects.filter(name__startswith='Beatles') <QuerySet [<Blog: Beatles Blog>]> # 列表中包含的是數據字典 >>> Blog.objects.filter(name__startswith='Beatles').values() <QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
該方法接收可選的位置參數*fields
,它指定values()應該限制哪些字段。若是指定字段,每一個字典將只包含指定的字段的鍵/值。若是沒有指定字段,每一個字典將包含數據庫表中全部字段的鍵和值。
例如:
>>> Blog.objects.values() <QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]> >>> Blog.objects.values('id', 'name') <QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>
values()方法還有關鍵字參數**expressions
,這些參數將傳遞給annotate()
:
>>> from django.db.models.functions import Lower >>> Blog.objects.values(lower_name=Lower('name')) <QuerySet [{'lower_name': 'beatles blog'}]>
在values()子句中的聚合應用於相同values()子句中的其餘參數以前。 若是須要按另外一個值分組,請將其添加到較早的values()子句中。 像這樣:
>>> from django.db.models import Count >>> Blog.objects.values('author', entries=Count('entry')) <QuerySet [{'author': 1, 'entries': 20}, {'author': 1, 'entries': 13}]> >>> Blog.objects.values('author').annotate(entries=Count('entry')) <QuerySet [{'author': 1, 'entries': 33}]>
注意:
若是你有一個字段foo是一個ForeignKey,默認的foo_id
參數返回的字典中將有一個叫作foo 的鍵,由於這是保存實際值的那個隱藏的模型屬性的名稱。 當調用foo_id
並傳遞字段的名稱,傳遞foo 或values()均可以,獲得的結果是相同的。像這樣:
>>> Entry.objects.values() <QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]> >>> Entry.objects.values('blog') <QuerySet [{'blog': 1}, ...]> >>> Entry.objects.values('blog_id') <QuerySet [{'blog_id': 1}, ...]>
當values()與distinct()一塊兒使用時,注意排序可能影響最終的結果。
若是values()子句位於extra()調用以後,extra()中的select參數定義的字段必須顯式包含在values()調用中。 values( 調用後面的extra( 調用將忽略選擇的額外的字段。
在values()以後調用only()和defer()不太合理,因此將引起一個NotImplementedError。
能夠經過ManyToManyField、ForeignKey 和 OneToOneFiel 屬性反向引用關聯的模型的字段:
>>> Blog.objects.values('name', 'entry__headline') <QuerySet [{'name': 'My blog', 'entry__headline': 'An entry'}, {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]>
values_list(*fields, flat=False)
與values()相似,只是在迭代時返回的是元組而不是字典。每一個元組包含傳遞給values_list()
調用的相應字段或表達式的值,所以第一個項目是第一個字段等。 像這樣:
>>> Entry.objects.values_list('id', 'headline') <QuerySet [(1, 'First entry'), ...]> >>> from django.db.models.functions import Lower >>> Entry.objects.values_list('id', Lower('headline')) <QuerySet [(1, 'first entry'), ...]>
若是隻傳遞一個字段,還能夠傳遞flat參數。 若是爲True,它表示返回的結果爲單個值而不是元組。 以下所示:
>>> Entry.objects.values_list('id').order_by('id') <QuerySet[(1,), (2,), (3,), ...]> >>> Entry.objects.values_list('id', flat=True).order_by('id') <QuerySet [1, 2, 3, ...]>
若是有多個字段,傳遞flat將發生錯誤。
若是不傳遞任何值給values_list()
,它將返回模型中的全部字段,以在模型中定義的順序。
常見的狀況是獲取某個模型實例的特定字段值。可使用values_list()
,而後調用get():
>>> Entry.objects.values_list('headline', flat=True).get(pk=1) 'First entry'
values()
和values_list()
都用於特定狀況下的優化:檢索數據子集,而無需建立模型實例。
注意經過ManyToManyField進行查詢時的行爲:
>>> Author.objects.values_list('name', 'entry__headline') <QuerySet [('Noam Chomsky', 'Impressions of Gaza'), ('George Orwell', 'Why Socialists Do Not Believe in Fun'), ('George Orwell', 'In Defence of English Cooking'), ('Don Quixote', None)]>
相似地,當查詢反向外鍵時,對於沒有任何做者的條目,返回None。
>>> Entry.objects.values_list('authors') <QuerySet [('Noam Chomsky',), ('George Orwell',), (None,)]>
dates(field, kind, order='ASC')
返回一個QuerySet,表示QuerySet內容中特定類型的全部可用日期的datetime.date
對象列表。
field參數是模型的DateField的名稱。 kind參數應爲"year","month"或"day"。 結果列表中的每一個datetime.date對象被截取爲給定的類型。
"year" 返回對應該field的全部不一樣年份值的列表。
"month"返回字段的全部不一樣年/月值的列表。
"day"返回字段的全部不一樣年/月/日值的列表。
order參數默認爲'ASC',或者'DESC'。 它指定如何排序結果。
例子:
>>> Entry.objects.dates('pub_date', 'year') [datetime.date(2005, 1, 1)] >>> Entry.objects.dates('pub_date', 'month') [datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)] >>> Entry.objects.dates('pub_date', 'day') [datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)] >>> Entry.objects.dates('pub_date', 'day', order='DESC') [datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)] >>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day') [datetime.date(2005, 3, 20)]
datetimes(field_name, kind, order='ASC', tzinfo=None)
返回QuerySet,爲datetime.datetime對象的列表,表示QuerySet內容中特定種類的全部可用日期。
field_name
應爲模型的DateTimeField的名稱。
kind參數應爲"hour","minute","month","year","second"或"day"。
結果列表中的每一個datetime.datetime對象被截取到給定的類型。
order參數默認爲'ASC',或者'DESC'。 它指定如何排序結果。
tzinfo參數定義在截取以前將數據時間轉換到的時區。
none()
調用none()將建立一個不返回任何對象的查詢集,而且在訪問結果時不會執行任何查詢。
例子:
>>> Entry.objects.none() <QuerySet []> >>> from django.db.models.query import EmptyQuerySet >>> isinstance(Entry.objects.none(), EmptyQuerySet) True
all()
返回當前QuerySet(或QuerySet子類)的副本。一般用於獲取所有QuerySet對象。
union(*other_qs, all=False)
Django中的新功能1.11。也就是集合中並集的概念!
使用SQL的UNION運算符組合兩個或更多個QuerySet的結果。例如:
>>> qs1.union(qs2, qs3)
默認狀況下,UNION操做符僅選擇不一樣的值。 要容許重複值,請使用all=True參數。
intersection(*other_qs)
Django中的新功能1.11。也就是集合中交集的概念!
使用SQL的INTERSECT運算符返回兩個或更多個QuerySet的共有元素。例如:
>>> qs1.intersection(qs2, qs3)
difference(*other_qs)
Django中的新功能1.11。也就是集合中差集的概念!
使用SQL的EXCEPT運算符只保留QuerySet中的元素,但不保留其餘QuerySet中的元素。例如:
>>> qs1.difference(qs2, qs3)
select_related(*fields)
沿着外鍵關係查詢關聯的對象的數據。這會生成一個複雜的查詢並引發性能的損耗,可是在之後使用外鍵關係時將不須要再次數據庫查詢。
下面的例子解釋了普通查詢和select_related()
查詢的區別。 下面是一個標準的查詢:
# 訪問數據庫。 e = Entry.objects.get(id=5) # 再次訪問數據庫以獲得關聯的Blog對象。 b = e.blog
下面是一個select_related
查詢:
# 訪問數據庫。 e = Entry.objects.select_related('blog').get(id=5) # 不會訪問數據庫,由於e.blog已經在前面的查詢中得到了。 b = e.blog
select_related()
可用於objects任何的查詢集:
from django.utils import timezone # Find all the blogs with entries scheduled to be published in the future. blogs = set() for e in Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog'): # 沒有select_related(),下面的語句將爲每次循環迭代生成一個數據庫查詢,以得到每一個entry關聯的blog。 blogs.add(e.blog)
filter()
和select_related()
的順序不重要。 下面的查詢集是等同的:
Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog') Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())
能夠沿着外鍵查詢。 若是有如下模型:
from django.db import models class City(models.Model): # ... pass class Person(models.Model): # ... hometown = models.ForeignKey( City, on_delete=models.SET_NULL, blank=True, null=True, ) class Book(models.Model): # ... author = models.ForeignKey(Person, on_delete=models.CASCADE)
調用Book.objects.select_related('author__hometown').get(id=4)
將緩存相關的Person 和相關的City:
b = Book.objects.select_related('author__hometown').get(id=4) p = b.author # Doesn't hit the database. c = p.hometown # Doesn't hit the database. b = Book.objects.get(id=4) # No select_related() in this example. p = b.author # Hits the database. c = p.hometown # Hits the database.
在傳遞給select_related()
的字段中,可使用任何ForeignKey和OneToOneField。
在傳遞給select_related
的字段中,還能夠反向引用OneToOneField。也就是說,能夠回溯到定義OneToOneField 的字段。 此時,可使用關聯對象字段的related_name
,而不要指定字段的名稱。
prefetch_related(*lookups)
在單個批處理中自動檢索每一個指定查找的相關對象。
與select_related
相似,可是策略是徹底不一樣的。
假設有這些模型:
from django.db import models class Topping(models.Model): name = models.CharField(max_length=30) class Pizza(models.Model): name = models.CharField(max_length=50) toppings = models.ManyToManyField(Topping) def __str__(self): # __unicode__ on Python 2 return "%s (%s)" % ( self.name, ", ".join(topping.name for topping in self.toppings.all()), )
並運行:
>>> Pizza.objects.all() ["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...
問題是每次QuerySet要求Pizza.objects.all()
查詢數據庫,所以self.toppings.all()
將在Pizza Pizza.__str__()
中的每一個項目的Toppings表上運行查詢。
可使用prefetch_related
減小爲只有兩個查詢:
>>> Pizza.objects.all().prefetch_related('toppings')
這意味着如今每次self.toppings.all()
被調用,不會再去數據庫查找,而是在一個預取的QuerySet緩存中查找。
還可使用正常鏈接語法來執行相關字段的相關字段。 假設在上面的例子中增長一個額外的模型:
class Restaurant(models.Model): pizzas = models.ManyToManyField(Pizza, related_name='restaurants') best_pizza = models.ForeignKey(Pizza, related_name='championed_by')
如下是合法的:
>>> Restaurant.objects.prefetch_related('pizzas__toppings')
這將預取全部屬於餐廳的比薩餅,和全部屬於那些比薩餅的配料。 這將致使總共3個查詢 - 一個用於餐館,一個用於比薩餅,一個用於配料。
>>> Restaurant.objects.prefetch_related('best_pizza__toppings')
這將獲取最好的比薩餅和每一個餐廳最好的披薩的全部配料。 這將在3個表中查詢 - 一個爲餐廳,一個爲「最佳比薩餅」,一個爲一個爲配料。
固然,也可使用best_pizza
來獲取select_related
關係,以將查詢數減小爲2:
>>> Restaurant.objects.select_related('best_pizza').prefetch_related('best_pizza__toppings')
extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
有些狀況下,Django的查詢語法難以簡單的表達複雜的WHERE子句,對於這種狀況,能夠在extra()生成的SQL從句中注入新子句。使用這種方法做爲最後的手段,這是一箇舊的API,在未來的某個時候可能被棄用。僅當沒法使用其餘查詢方法表達查詢時才使用它。
例如:
>>> qs.extra( ... select={'val': "select col from sometable where othercol = %s"}, ... select_params=(someparam,), ... )
至關於:
>>> qs.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))
defer(*fields)
在一些複雜的數據建模狀況下,模型可能包含大量字段,其中一些可能包含大尺寸數據(例如文本字段),將它們轉換爲Python對象須要花費很大的代價。
當最初獲取數據時不知道是否須要這些特定字段的狀況下,若是正在使用查詢集的結果,能夠告訴Django不要從數據庫中檢索它們。
經過傳遞字段名稱到defer()實現不加載:
Entry.objects.defer("headline", "body")
具備延遲加載字段的查詢集仍將返回模型實例。
每一個延遲字段將在你訪問該字段時從數據庫中檢索(每次只檢索一個,而不是一次檢索全部的延遲字段)。
能夠屢次調用defer()。 每一個調用都向延遲集添加新字段:
# 延遲body和headline兩個字段。 Entry.objects.defer("body").filter(rating=5).defer("headline")
字段添加到延遲集的順序可有可無。對已經延遲的字段名稱再次defer()沒有問題(該字段仍將被延遲)。
可使用標準的雙下劃線符號來分隔關聯的字段,從而加載關聯模型中的字段:
Blog.objects.select_related().defer("entry__headline", "entry__body")
若是要清除延遲字段集,將None做爲參數傳遞到defer():
# 當即加載全部的字段。 my_queryset.defer(None)
defer()方法(及其兄弟,only())僅適用於高級用例,它們提供了數據加載的優化方法。
only(*fields)
only()方法與defer()相反。
若是有一個模型幾乎全部的字段須要延遲,使用only()指定補充的字段集可使代碼更簡單。
假設有一個包含字段biography、age和name的模型。 如下兩個查詢集是相同的,就延遲字段而言:
Person.objects.defer("age", "biography") Person.objects.only("name")
每當你調用only()時,它將替換當即加載的字段集。所以,對only()的連續調用的結果是隻有最後一次調用的字段被考慮:
# This will defer all fields except the headline. Entry.objects.only("body", "rating").only("headline")
因爲defer()以遞增方式動做(向延遲列表中添加字段),所以你能夠結合only()和defer()調用:
# Final result is that everything except "headline" is deferred. Entry.objects.only("headline", "body").defer("body") # Final result loads headline and body immediately (only() replaces any # existing set of fields). Entry.objects.defer("body").only("headline", "body")
當對具備延遲字段的實例調用save()時,僅保存加載的字段。
using(alias)
若是正在使用多個數據庫,這個方法用於指定在哪一個數據庫上查詢QuerySet。方法的惟一參數是數據庫的別名,定義在DATABASES。
例如:
# queries the database with the 'default' alias. >>> Entry.objects.all() # queries the database with the 'backup' alias >>> Entry.objects.using('backup')
select_for_update(nowait=False, skip_locked=False)
返回一個鎖住行直到事務結束的查詢集,若是數據庫支持,它將生成一個SELECT ... FOR UPDATE
語句。
例如:
entries = Entry.objects.select_for_update().filter(author=request.user)
全部匹配的行將被鎖定,直到事務結束。這意味着能夠經過鎖防止數據被其它事務修改。
通常狀況下若是其餘事務鎖定了相關行,那麼本查詢將被阻塞,直到鎖被釋放。使用select_for_update(nowait=True)
將使查詢不阻塞。若是其它事務持有衝突的鎖,那麼查詢將引起DatabaseError
異常。也可使用select_for_update(skip_locked=True)
忽略鎖定的行。nowait和skip_locked
是互斥的。
目前,postgresql,oracle和mysql數據庫後端支持select_for_update()
。可是,MySQL不支持nowait和skip_locked
參數。
raw(raw_query, params=None, translations=None)
接收一個原始的SQL查詢,執行它並返回一個django.db.models.query.RawQuerySet
實例。
這個RawQuerySet實例能夠迭代,就像普通的QuerySet同樣。