QuerySet 能夠被構造,過濾,切片,作爲參數傳遞,這些行爲都不會對數據庫進行操做。只要你查詢的時候才真正的操做數據庫。python
下面的 QuerySet 行爲會致使執行查詢的操做:正則表達式
循環(Iteration):QuerySet 是可迭代的,在你遍歷對象時就會執行數據庫操做。例如,打印出全部博文的大標題:算法
for e in Entry.objects.all(): print(e.headline)
切片(Slicing): QuerySet 是能夠用 Python 的數組切片語法完成切片。通常來講對一個未查詢的 QuerySet 切片就返回另外一個未查詢的 QuerySet (新 QuerySet 不會被執行)。不過若是你在切片時使用了 "step" 參數,Django 仍會執行數據庫操做而且返回列表對象。對一個查詢過的QuerySet執行切片也會返回列表對象。數據庫
序列化/緩存化(Pickling/Caching): 詳情請查看 pickling QuerySets。 這一節所強調的一點是查詢結果是從數據庫中讀取的。django
repr(). 調用 QuerySet 的 repr() 方法時,查詢就會被運行。這對於 Python 命令行來講很是方便,你可使用 API 當即看到查詢結果。後端
len(). 調用 QuerySet 的 len() 方法,查詢就會被運行。這正如你所料,會返回查詢結果列表的長度。數組
注意:若是你想獲得集合中記錄的數量,就不要使用 QuerySet 的 len() 方法。由於直接在數據庫層面使用 SQL 的 SELECT COUNT(*) 會更加高效,Django 提供了 count() 方法就是這個緣由。詳情參閱下面的 count() 方法。緩存
list(). 對 QuerySet 應用 list() 方法,就會運行查詢。例如:安全
entry_list = list(Entry.objects.all())
要注意地是:使用這個方法會佔用大量內存,由於 Django 將列表內容都載入到內存中。作爲對比,遍歷 QuerySet 是從數據庫讀取數據,僅在使用某個對象時纔將其載入到內容中。ide
Pickling QuerySets
若是你要 序列化(pickle) 一個 QuerySet,Django 首先就會將查詢對象載入到內存中以完成序列化,這樣你就能夠第一時間使用對象(直接從數據庫中讀取數據須要必定時間,這正是緩存所想避免的)。而序列化是緩存化的先行工做,因此在緩存查詢時,首先就會進行序列化工做。這意味着當你反序列化 QuerySet 時,第一時間就會從內存中得到查詢的結果,而不是從數據庫中查找。
若是你只是想序列化部分必要的信息以便晚些時候能夠從數據庫中重建 Queryset ,那隻序列化 QuerySet 的 query 屬性便可。接下來你就可使用下面的代碼重建原來的 QuerySet (這當中沒有數據庫讀取):
>>> import pickle >>> query = pickle.loads(s) # Assuming 's' is the pickled string. >>> qs = MyModel.objects.all() >>> qs.query = query # Restore the original 'query'.
query 屬性是一個不透明的對象。這就意味着它的內部結構並非公開的。即使如此,對於本節提到的序列化和反序列化來講,它還是安全和被徹底支持的。
filter(**kwargs)
返回一個新的 QuerySet ,它包含了與所給的篩選條件相匹配的對象。
這些篩選條件(**kwargs)在下面的字段篩選(Field lookups) 中有詳細介紹。多個條件之間在 SQL 語句中是 AND 關係。
exclude(**kwargs)
返回一個新的 QuerySet,它包含那些與所給篩選條件不匹配的對象。
這些篩選條件(**kwargs)也在下面的 字段篩選(Field lookups) 中有詳細描述。多個條件之間在 SQL 語句中也是 AND 關係,可是總體又是一個 NOT() 關係。
下面的例子剔除了出版日期 pub_date 晚於 2005-1-3 而且大標題 headline 是 "Hello" 的全部博文(entry):
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
在 SQL 語句中,這等價於:
SELECT ... WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')
下面的例子剔除出版日期 pub_date 晚於 2005-1-3 或者大標題是 "Hello" 的全部博文:
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')
在 SQL 語句中,這等價於:
SELECT ... WHERE NOT pub_date > '2005-1-3' OR NOT headline = 'Hello'
要注意第二個例子是有不少限制的。
annotate(*args, **kwargs)
咱們能夠爲 QuerySet 中的每一個對象添加註解。能夠經過計算查詢結果中每一個對象所關聯的對象集合,從而得出總計值(也能夠是平均值或總和,等等),作爲 QuerySet 中對象的註解。annotate() 中的每一個參數都會被作爲註解添加到 QuerySet 中返回的對象。
Django 提供的註解函式在下面的 (註解函式Aggregation Functions) 有詳細介紹。
註解會使用關鍵字參數來作爲註解的別名。其中任何參數都會生成一個別名,它與註解函式的名稱和被註解的 model 相關。
例如,你正在操做一個博客列表,你想知道一個博客究竟有多少篇博文:
>>> 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 model 類自己並無定義 entry__count 屬性,但可使用註解函式的關係字參數,從而改變註解的命名:
>>> 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)
默認狀況下, QuerySet 返回的查詢結果是根據 model 類的 Meta 設置所提供的 ordering 項中定義的排序元組來進行對象排序的。你可使用 order_by 方法覆蓋以前 QuerySet 中的排序設置。
例如:
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')
返回結果就會先按照 pub_date 進行升序排序,再按照 headline 進行降序排序。 "-pub_date" 前面的負號"-"表示降序排序。默認是採用升序排序。要隨機排序,就使用 "?",例如:
Entry.objects.order_by('?')
注意:order_by('?') 可能會很是緩慢和消耗過多資源,這取決於你所使用的數據庫。
要根據其餘 model 字段排序,所用語法和跨關係查詢的語法相同。就是說,用兩個連續的下劃線(__)鏈接關聯 model 和 要排序的字段名稱, 並且能夠一直延伸。例如:
Entry.objects.order_by('blog__name', 'headline')
若是你想對關聯字段排序,在沒有指定 Meta.ordering 的狀況下,Django 會採用默認排序設置,就是按照關聯 model 的主鍵進行排序。例如:
Entry.objects.order_by('blog')
等價於:
Entry.objects.order_by('blog__id')
這是由於 Blog model 沒有聲明排序項的原故。
Django 1.7新添加:
# No Join Entry.objects.order_by('blog_id') #能夠避免JOIN的代價 # Join Entry.objects.order_by('blog__id')
若是你使用了 distinct() 方法,那麼在對關聯字段排序時要格外謹慎。
在 Django 當中是能夠按照多值字段(例如 ManyToMany 字段)進行排序的。不過,這個特性雖然先進,可是並不實用。除非是你已經很清楚過濾結果或可用數據中的每一個對象,都只有一個相關聯的對象時(就是至關於只是一對一關係時),排序纔會符合你預期的結果,因此對多值字段排序時要格外注意。
若是你不想對任何字段排序,也不想使用 model 中原有的排序設置,那麼能夠調用無參數的 order_by() 方法。
對於排序項是否應該大小寫敏感,Django 並無提供設置方法,這徹底取決於後端的數據庫對排序大小寫如何處理。
你能夠令某個查詢結果是可排序的,也能夠是不可排序的,這取決於 QuerySet.ordered 屬性。若是它的值是 True ,那麼 QuerySet 就是可排序的。
reverse()
使用 reverse() 方法會對查詢結果進行反向排序。調用兩次 reverse() 方法至關於排序沒發生改過。
要獲得查詢結果中最後五個對象,能夠這樣寫:
my_queryset.reverse()[:5]
要注意這種方式與 Python 語法中的從尾部切片是徹底不同的。在上面的例子中,是先獲得最後一個元素,而後是倒數第二個,依次處理。可是若是咱們有一個 Python 隊列,使用 seq[-5:]時,倒是先獲得倒數第五個元素。Django 之因此採用 reverse 來獲取倒數的記錄,而不支持切片的方法,緣由就是後者在 SQL 中難以作好。
還有一點要注意,就是 reverse() 方法應該只做用於已定義了排序項 QuerySet (例如,在查詢時使用了order_by()方法,或是在 model 類當中直接定義了排序項). 若是並無明肯定義排序項,那麼調用 QuerySet, calling reverse() 就沒什麼實際意義(由於在調用 reverse() 以前,數據沒有定義排序,因此在這以後也不會進行排序。)
distinct()
返回一個新的 QuerySet ,它會在執行 SQL 查詢時使用 SELECT DISTINCT。這意味着返回結果中的重複記錄將被剔除。
默認狀況下, QuerySet 並會剔除重複的記錄。在實際當中,這不是什麼問題,由於象 Blog.objects.all() 這樣的查詢並不會產生重複的記錄。可是,若是你使用 QuerySet 作多表查詢時,就極可能會產生重複記錄。這時,就可使用 distinct() 方法。
Note
在 order_by(*fields) 中出現的字段也會包含在 SQL SELECT 列中。若是和 distinct() 同時使用,有時返回的結果卻與預想的不一樣。這是由於:若是你對跨關係的關聯字段進行排序,這些字段就會被添加到被選取的列中,這就可能產生重複數據(好比,其餘的列數據都相同,只是關聯字段的值不一樣)。但因爲 order_by 中的關聯字段並不會出如今返回結果中(他們僅僅是用來實現order),因此有時返回的數據看上去就象是並無進行過 distinct 處理同樣。
一樣的緣由,若是你用 values() 方法得到被選取的列,就會發現包含在 order_by() (或是 model 類的 Meta 中設置的排序項)中的字段也包含在裏面,就會對返回的結果產生影響。
這章節強調的就是在你使用 distinct() 時,要謹慎對待關聯字段排序。一樣的,在同時使用 distinct() 和 values() 時,若是排序字段並無出如今 values() 返回的結果中,那麼也要引發注意。
values(*fields)
返回一個 ValuesQuerySet ----一個特殊的 QuerySet ,運行後獲得的並非一系列 model 的實例化對象,而是一個可迭代的字典序列。
每一個字典都表示一個對象,而鍵名就是 model 對象的屬性名稱。
下面的例子就對 values() 獲得的字典與傳統的 model 對象進行了對比:
# This list contains a Blog object. >>> Blog.objects.filter(name__startswith='Beatles') [<Blog: Beatles Blog>] # This list contains a dictionary. >>> Blog.objects.filter(name__startswith='Beatles').values() [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]
values() 能夠接收可選的位置參數,*fields,就是字段的名稱,用來限制 SELECT 選取的數據。若是你指定了字段參數,每一個字典就會以 Key-Value 的形式保存你所指定的字段信息;若是沒有指定,每一個字典就會包含當前數據表當中的全部字段信息。
例如:
>>> Blog.objects.values() [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}], >>> Blog.objects.values('id', 'name') [{'id': 1, 'name': 'Beatles Blog'}]
下面這些細節值得注意:
若是你有一個名爲 foo 的ForeignKey 字段,默認狀況下調用 values() 返回的字典中包含鍵名爲 foo_id 的字典項,由於它是一個隱含的 model 字段,用來保存關聯對象的主鍵值( foo 屬性用來聯繫相關聯的 model )。當你使用 values() 並傳遞字段名稱時, 傳遞foo 或 foo_id 都會獲得相同的結果 (字典中的鍵名會自動換成你傳遞的字段名稱)。
例如:
>>> Entry.objects.values() [{'blog_id': 1, 'headline': u'First Entry', ...}, ...] >>> Entry.objects.values('blog') [{'blog': 1}, ...] >>> Entry.objects.values('blog_id') [{'blog_id': 1}, ...]
在 values() 和 distinct() 同時使用時,要注意排序項會影響返回的結果,詳情請查看上面 distinct() 一節。
在values()以後使用defer()和only()是無用的。
ValuesQuerySet 是很是有用的。利用它,你就能夠只得到你所需的那部分數據,而沒必要同時讀取其餘的無用數據。
最後,要提醒的是,ValuesQuerySet 是 QuerySet 的一個子類,因此它擁有 QuerySet 全部的方法。你能夠對它調用 filter() 或是 order_by() 以及其餘方法。因此下面倆種寫法是等價的:
Blog.objects.values().order_by('id') Blog.objects.order_by('id').values()
Django 的編寫者們更喜歡第二種寫法,就是先寫影響 SQL 的方法,再寫影響輸出的方法(好比例中先寫 order,再寫values ),但這些都可有可無,徹底視你我的喜愛而定。
也能夠指向一對1、多對多、外鍵關係對象的域:
Blog.objects.values('name', 'entry__headline') [{'name': 'My blog', 'entry__headline': 'An entry'}, {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]
當指向多對多關係時,由於關聯對象可能有不少,因此同一個對象會根據不一樣的多對多關係返回屢次。
values_list(*fields)
它與 values() 很是類似,只不事後者返回的結果是字典序列,而 values() 返回的結果是元組序列。每一個元組都包含傳遞給 values_list() 的字段名稱和內容。好比第一項就對應着第一個字段,例如:
>>> Entry.objects.values_list('id', 'headline') [(1, u'First entry'), ...]
若是你傳遞了一個字段作爲參數,那麼你可使用 flat 參數。若是它的值是 True,就意味着返回結果都是單獨的值,而不是元組。下面的例子會講得更清楚:
>>> Entry.objects.values_list('id').order_by('id') [(1,), (2,), (3,), ...] >>> Entry.objects.values_list('id', flat=True).order_by('id') [1, 2, 3, ...]
若是傳遞的字段不止一個,使用 flat 就會致使錯誤。
若是你沒給 values_list() 傳遞參數,它就會按照字段在 model 類中定義的順序返回全部的字段。
注意這個方法返回的是 ValuesListQuerySet對象,和列表類似但並非列表,須要列表操做時需list()轉爲列表。
dates(field, kind, order='ASC')
返回一個 DateQuerySet ,就是提取 QuerySet 查詢中所包含的日期,將其組成一個新的 datetime.date 對象的列表。
field 是你的 model 中的 DateField 字段名稱。
kind 是 "year", "month" 或 "day" 之一。 每一個 datetime.date對象都會根據所給的 type 進行截減。
"year" 返回全部時間值中非重複的年分列表。
"month" 返回全部時間值中非重複的年/月列表。
"day" 返回全部時間值中非重複的年/月/日列表。
order, 默認是 'ASC',只有兩個取值 '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, kind, order='ASC')
返回一個 DateTimeQuerySet ,就是提取 QuerySet 查詢中所包含的日期,將其組成一個新的 datetime.datetime 對象的列表。
none()
返回一個 EmptyQuerySet -- 它是一個運行時只返回空列表的 QuerySet。它常常用在這種場合:你要返回一個空列表,可是調用者卻須要接收一個 QuerySet 對象。(這時,就能夠用它代替空列表)
例如:
>>> Entry.objects.none() [] >>> from django.db.models.query import EmptyQuerySet >>> isinstance(Entry.objects.none(), EmptyQuerySet) True
all()
返回當前 QuerySet (或者是傳遞的 QuerySet 子類)的一分拷貝。 這在某些場合是很用的,好比,你想對一個 model manager 或是一個 QuerySet 的查詢結果作進一步的過濾。你就能夠調用 all() 得到一分拷貝以繼續操做,從而保證原 QuerySet 的安全。
當一個QuerySet查詢後,它會緩存查詢結果。若是數據庫發生了改變,就能夠調用all()來更新查詢過的QuerySet。
select_related()
返回一個 QuerySet ,它會在執行查詢時自動跟蹤外鍵關係,從而選取所關聯的對象數據。它是一個增效器,雖然會致使較大的數據查詢(有時會很是大),可是接下來再使用外鍵關係得到關聯對象時,就會再也不次讀取數據庫了。
下面的例子展現在得到關聯對象時,使用 select_related() 和不使用的區別,首先是不使用的例子:
# Hits the database. e = Entry.objects.get(id=5) # Hits the database again to get the related Blog object. b = e.blog
接下來是使用 select_related 的例子:
# Hits the database. e = Entry.objects.select_related().get(id=5) # Doesn't hit the database, because e.blog has been prepopulated # in the previous query. b = e.blog
select_related() 會盡量地深刻遍歷外鍵鏈接。例如:
from django.db import models class City(models.Model): # ... pass class Person(models.Model): # ... hometown = models.ForeignKey(City) class Book(models.Model): # ... author = models.ForeignKey(Person)
接下來調用 Book.objects.select_related().get(id=4) 將緩存關聯的 Person 和 City:
b = Book.objects.select_related('person__city').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.
prefetch_related()
對於多對多字段(ManyToManyField)和一對多字段,可使用prefetch_related()來進行優化。或許你會說,沒有一個叫OneToManyField的東西啊。實際上 ,ForeignKey就是一個多對一的字段,而被ForeignKey關聯的字段就是一對多字段了。
prefetch_related()和select_related()的設計目的很類似,都是爲了減小SQL查詢的數量,可是實現的方式不同。後者是經過JOIN語句,在SQL查詢內解決問題。可是對於多對多關係,使用SQL語句解決就顯得有些不太明智,由於JOIN獲得的表將會很長,會致使SQL語句運行時間的增長和內存佔用的增長。如有n個對象,每一個對象的多對多字段對應Mi條,就會生成Σ(n)Mi 行的結果表。
prefetch_related()的解決方法是,分別查詢每一個表,而後用Python處理他們之間的關係。
例如:
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().prefetch_related('toppings')
能夠聯合查詢:
class Restaurant(models.Model): pizzas = models.ManyToMany(Pizza, related_name='restaurants') best_pizza = models.ForeignKey(Pizza, related_name='championed_by')
下面的例子均可以
>>> Restaurant.objects.prefetch_related('pizzas__toppings') >>> Restaurant.objects.prefetch_related('best_pizza__toppings') >>> 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 子句。對於這種狀況,Django 提供了 extra() QuerySet 修改機制,它能在QuerySet 生成的 SQL 從句中注入新子句。
因爲產品差別的緣由,這些自定義的查詢難以保障在不一樣的數據庫之間兼容(由於你手寫 SQL 代碼的緣由),並且違背了 DRY 原則,因此如非必要,仍是儘可能避免寫 extra。
在 extra 能夠指定一個或多個 params 參數,如 select,where 或 tables。全部參數都是可選的,但你至少要使用一個。
select
select 參數可讓你在 SELECT 從句中添加其餘字段信息。它應該是一個字典,存放着屬性名到 SQL 從句的映射。
例如:
Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
結果中每一個 Entry 對象都有一個額外的 is_recent 屬性,它是一個布爾值,表示 pub_date 是否晚於2006年1月1號。
Django 會直接在 SELECT 中加入對應的 SQL 片段,因此轉換後的 SQL 以下:
SELECT blog_entry.*, (pub_date > '2006-01-01') FROM blog_entry;
下面這個例子更復雜一些;它會在每一個 Blog 對象中添加一個 entry_count 屬性,它會運行一個子查詢,獲得相關聯的 Entry 對象的數量:
Blog.objects.extra( select={ 'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id' }, )
(在上面這個特例中,咱們要了解這個事實,就是 blog_blog 表已經存在於 FROM 從句中。)
翻譯成 SQL 以下:
SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) AS entry_count FROM blog_blog;
要注意的是,大多數數據庫須要在子句兩端添加括號,而在 Django 的 select 從句中卻無須這樣。一樣要引發注意的是,在某些數據庫中,好比某些 MySQL 版本,是不支持子查詢的。
某些時候,你可能想給 extra(select=...) 中的 SQL 語句傳遞參數,這時就可使用 select_params 參數。由於 select_params 是一個隊列,而 select 屬性是一個字典,因此二者在匹配時應正確地一一對應。在這種狀況下中,你應該使用 django.utils.datastructures.SortedDict 匹配 select 的值,而不是使用通常的 Python 隊列。
例如:
Blog.objects.extra( select=SortedDict([('a', '%s'), ('b', '%s')]), select_params=('one', 'two'))
在使用 extra() 時要避免在 select 字串含有 "%%s" 子串, 這是由於在 Django 中,處理 select 字串時查找的是 %s 而並不是轉義後的 % 字符。因此若是對 % 進行了轉義,反而得不到正確的結果。
where / tables
你可使用 where 參數顯示定義 SQL 中的 WHERE 從句,有時也能夠運行非顯式地鏈接。你還可使用 tables 手動地給 SQL FROM 從句添加其餘表。
where 和 tables 都接受字符串列表作爲參數。全部的 where 參數彼此之間都是 "AND" 關係。
例如:
Entry.objects.extra(where=['id IN (3, 4, 5, 20)'])
大體能夠翻譯爲以下的 SQL:
SELECT * FROM blog_entry WHERE id IN (3, 4, 5, 20);
在使用 tables 時,若是你指定的表在查詢中已出現過,那麼要格外當心。當你經過 tables 參數添加其餘數據表時,若是這個表已經被包含在查詢中,那麼 Django 就會認爲你想再一次包含這個表。這就致使了一個問題:因爲重複出現屢次的表會被賦予一個別名,因此除了第一次以外,每一個重複的表名都會分別由 Django 分配一個別名。因此,若是你同時使用了 where 參數,在其中用到了某個重複表,殊不知它的別名,那麼就會致使錯誤。
通常狀況下,你只會添加一個未在查詢中出現的新表。可是若是上面所提到的特殊狀況發生了,那麼能夠採用以下措施解決。首先,判斷是否有必要要出現重複的表,可否將重複的表去掉。若是這點行不通,就試着把 extra() 調用放在查詢結構的起始處,由於首次出現的表名不會被重命名,因此可能能解決問題。若是這也不行,那就查看生成的 SQL 語句,從中找出各個數據庫的別名,而後依此重寫 where 參數,由於只要你每次都用一樣的方式調用查詢(queryset),表的別名都不會發生變化。因此你能夠直接使用表的別名來構造 where。
order_by
若是你已經過 extra() 添加了新字段或是數據庫,此時若想對新字段進行排序,就能夠給 extra() 中的 order_by 參數傳遞一個排序字符串序列。字符串能夠是 model 原生的字段名(與使用普通的 order_by() 方法同樣),也能夠是 table_name.column_name 這種形式,或者是你在 extra() 的 select 中所定義的字段。
例如:
q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"}) q = q.extra(order_by = ['-is_recent'])
這段代碼按照 is_recent 對記錄進行排序,字段值是 True 的排在前面,False 的排在後面。(True 在降序排序時是排在 False 的前面)。
順便說一下,上面這段代碼同時也展現出,能夠依你所願的那樣屢次調用 extra() 操做(每次添加新的語句結構便可)。
params
上面提到的 where 參數還能夠用標準的 Python 佔位符 -- '%s' ,它能夠根據數據庫引擎自動決定是否添加引號。 params 參數是用來替換佔位符的字符串列表。
例如:
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
使用 params 替換 where 的中嵌入值是一個很是好的作法,這是由於 params 能夠根據你的數據庫判斷要不要給傳入值添加引號(例如,傳入值中的引號會被自動轉義)。
很差的用法:
Entry.objects.extra(where=["headline='Lennon'"])
優雅的用法:
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
defer(*fields)
在某些數據複雜的環境下,你的 model 可能包含很是多的字段,可能某些字段包含很是多的數據(好比,文檔字段),或者將其轉化爲 Python 對象會消耗很是多的資源。在這種狀況下,有時你可能並不須要這種字段的信息,那麼你可讓 Django 不讀取它們的數據。
將不想載入的字段的名稱傳給 defer() 方法,就能夠作到延後載入:
Entry.objects.defer("lede", "body")
延後截入字段的查詢返回的還是 model 類的實例。在你訪問延後載入字段時,你仍能夠得到字段的內容,所不一樣的是,內容是在你訪問延後字段時纔讀取數據庫的,而普通字段是在運行查詢(queryset)時就一次性從數據庫中讀取數據的。
你能夠屢次調用 defer() 方法。每一個調用均可以添加新的延後載入字段:
# Defers both the body and lede fields. Entry.objects.defer("body").filter(headline="Lennon").defer("lede")
對延後載入字段進行排序是不會起做用的;重複添加延後載入字段也不會有何不良影響。
你也能夠延後載入關聯 model 中的字段(前提是你使用 select_related() 載入了關聯 model),用法也是用雙下劃線鏈接關聯字段:
Blog.objects.select_related().defer("entry__lede", "entry__body")
若是你想清除延後載入的設置,只要使用將 None 作爲參數傳給 defer() 便可:
# Load all fields immediately. my_queryset.defer(None)
有些字段不管你如何指定,都不會被延後加載。好比,你永遠不能延後加載主鍵字段。若是你使用 select_related() 得到關聯 model 字段信息,那麼你就不能延後載入關聯 model 的主鍵。(若是這樣作了,雖然不會拋出錯誤,事實上卻不完成延後加載)
Note
defer() 方法(和隨後提到的 only() 方法) 都只適用於特定狀況下的高級屬性。它們能夠提供性能上的優化,不過前提是你已經對你用到的查詢有過很深刻細緻的分析,很是清楚你須要的到底是哪些信息,並且已經對你所須要的數據和默認狀況下返回的全部數據進行比對,清楚二者之間的差別。這完成了上述工做以後,再使用這兩種方法進行優化纔是有意義的。因此當你剛開始構建你的應用時,先不要急着使用 defer() 方法,等你已經寫完查詢而且分析成哪些方面是熱點應用之後,再用也不遲。
only(*fields)
only() 方法或多或少與 defer() 的做用相反。若是你在提取數據時但願某個字段不該該被延後載入,而應該當即載入,那麼你就能夠作使用 only() 方法。若是你一個 model ,你但願它全部的字段都延後加載,只有某幾個字段是當即載入的,那麼你就應該使用 only() 方法。
若是你有一個 model,它有 name, age 和 biography 三個字段,那麼下面兩種寫法效果同樣的:
Person.objects.defer("age", "biography") Person.objects.only("name")
你不管什麼時候調用 only(),它都會馬上更改載入設置。這與它的命名很是相符:只有 only 中的字段會當即載入,而其餘的則都是延後載入的。所以,連續調用 only() 時,只有最後一個 only 方法纔會生效:
# This will defer all fields except the headline. Entry.objects.only("body", "lede").only("headline")
因爲 defer() 能夠遞增(每次都添加字段到延後載入的列表中),因此你能夠將 only() 和 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")
using(alias)
使用多個數據庫時使用,參數是數據庫的alias
# 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)
返回queryset,並將須要更新的行鎖定,相似於SELECT ... FOR UPDATE的操做。
entries = Entry.objects.select_for_update().filter(author=request.user)
全部匹配的entries都會被鎖定直到這次事務結束。
raw(raw_query, params=None, translations=None)
執行raw SQL queries
下面所列的 QuerySet 方法做用於 QuerySet,卻並不返回 other than a QuerySet。
這些方法並不使用緩存(請查看 緩存與查詢(Caching and QuerySets))。因此它們在運行時是當即讀取數據庫的。
get(**kwargs)
返回與所給的篩選條件相匹配的對象,篩選條件在 字段篩選條件(Field lookups) 一節中有詳細介紹。
在使用 get() 時,若是符合篩選條件的對象超過一個,就會拋出 MultipleObjectsReturned 異常。MultipleObjectsReturned 是 model 類的一個屬性。
在使用 get() 時,若是沒有找到符合篩選條件的對象,就會拋出 DoesNotExist 異常。這個異常也是 model 對象的一個屬性。例如:
Entry.objects.get(id='foo') # raises Entry.DoesNotExist
DoesNotExist 異常繼承自 django.core.exceptions.ObjectDoesNotExist,因此你能夠直接截獲 DoesNotExist 異常。例如:
from django.core.exceptions import ObjectDoesNotExist try: e = Entry.objects.get(id=3) b = Blog.objects.get(id=1) except ObjectDoesNotExist: print("Either the entry or blog doesn't exist.")
create(**kwargs)
建立對象並同時保存對象的快捷方法:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
和
p = Person(first_name="Bruce", last_name="Springsteen") p.save(force_insert=True)
是相同的。
force_insert 參數在別處有詳細介紹,它表示把當前 model 當成一個新對象來建立。通常狀況下,你沒必要擔憂這一點,可是若是你的 model 的主鍵是你手動指定的,並且它的值已經在數據庫中存在,那麼調用 create() 就會失敗,並拋出 IntegrityError。這是由於主鍵值必須是惟一的。因此當你手動指定主鍵時,記得要作好處理異常的準備。
get_or_create(defaults=None,**kwargs)
這是一個方便實際應用的方法,它根據所給的篩選條件查詢對象,若是對象不存在就建立一個新對象。
它返回的是一個 (object, created) 元組,其中的 object 是所讀取或是建立的對象,而 created 則是一個布爾值,它表示前面提到的 object 是不是新建立的。
這意味着它能夠有效地減小代碼,而且對編寫數據導入腳本很是有用。例如:
try: obj = Person.objects.get(first_name='John', last_name='Lennon') except Person.DoesNotExist: obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9)) obj.save()
上面的代碼會隨着 model 中字段數量的激增而變得愈發庸腫。接下來用 get_or_create() 重寫:
obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon', defaults={'birthday': date(1940, 10, 9)})
在這裏要注意 defaults 是一個字典,它僅適用於建立對象時爲字段賦值,而並不適用於查找已存在的對象。 get_or_create() 所接收的關鍵字參數都會在調用 get() 時被使用,有一個參數例外,就是 defaults。在使用get_or_create() 時若是找到了對象,就會返回這個對象和 False。若是沒有找到,就會實例化一個新對象,並將其保存;同時返回這個新對象和 True。建立新對象的步驟大體以下:
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k]) params.update(defaults) obj = self.model(**params) obj.save()
用天然語言描述:從非 'defaults' 關鍵字參數中排除含有雙下劃線的參數(由於雙下劃線表示非精確查詢),而後再添加 defaults 字典的內容,若是鍵名與已有的關鍵字參數重複,就以 defaults 中的內容爲準, 而後將整理後的關鍵字參數傳遞給 model 類。固然,這只是算法的簡化描述,實際上對不少細節沒有說起,好比對異常和邊界條件的處理。若是你對此感興趣,不妨看一下原代碼。
若是你的 model 恰巧有一個字段,名稱正是 defaults,並且你想在 get_or_create() 中用它作爲精確查詢的條件, 就得使用 'defaults__exact' (以前提過 defaults 只能在建立時對對象賦值,而不能進行查詢),象下面這樣:
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
若是你手動指定了主鍵,那麼使用 get_or_create() 方法時也會象 create() 同樣,拋出相似的異常。當你手動指定了主鍵,若主鍵值已經在數據庫中存在,就會拋出一個 IntegrityError 異常。
最後提一下在 Django 視圖(views)中使用 get_or_create() 時要注意的一點。如上所說,對於在腳本中分析數據和添加新數據而言,get_or_create() 是很是有用的。可是若是你是在視圖中使用 get_or_create() ,那麼就要格外留意,要確認是在 POST 請求中使用,除非你有很必要和很充分的理由纔不這麼作。而在 GET 請求中使用的話,不會對數據產生任何做用。而使用 POST 的話,每一個發往頁面的請求都會對數據有必定的反作用。
Note
經過多對多關係使用時要注意:
class Chapter(models.Model): title = models.CharField(max_length=255, unique=True) class Book(models.Model): title = models.CharField(max_length=256) chapters = models.ManyToManyField(Chapter)
運行:
>>> book = Book.objects.create(title="Ulysses") >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, True) >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, False) >>> Chapter.objects.create(title="Chapter 1") <Chapter: Chapter 1> >>> book.chapters.get_or_create(title="Chapter 1") # Raises IntegrityError
不和book相關聯的Chapter就不會被查找到了。
update_or_create(defaults=None, **kwargs)
與上面相似
try: obj = Person.objects.get(first_name='John', last_name='Lennon') for key, value in updated_values.iteritems(): setattr(obj, key, value) obj.save() except Person.DoesNotExist: updated_values.update({'first_name': 'John', 'last_name': 'Lennon'}) obj = Person(**updated_values) obj.save()
能夠簡寫爲:
obj, created = Person.objects.update_or_create( first_name='John', last_name='Lennon', defaults=updated_values)
bulk_create(objs, batch_size=None)
批量建立
>>> Entry.objects.bulk_create([ ... Entry(headline="Django 1.0 Released"), ... Entry(headline="Django 1.1 Announced"), ... Entry(headline="Breaking: Django is awesome") ... ])
優於:
Entry.objects.create(headline="Python 1.0 Released") Entry.objects.create(headline="Python 1.1 Planned")
count()
返回數據庫中匹配查詢(QuerySet)的對象數量。 count() 不會拋出任何異常。
例如:
# Returns the total number of entries in the database. Entry.objects.count() # Returns the number of entries whose headline contains 'Lennon' Entry.objects.filter(headline__contains='Lennon').count()
count() 會在後端執行 SELECT COUNT(*) 操做,因此你應該儘可能使用 count() 而不是對返回的查詢結果使用 len() 。
根據你所使用的數據庫(例如 PostgreSQL 和 MySQL),count() 可能會返回長整型,而不是普通的 Python 整數。這確實是一個很古怪的舉措,沒有什麼實際意義。
in_bulk(id_list)
接收一個主鍵值列表,而後根據每一個主鍵值所其對應的對象,返回一個主鍵值與對象的映射字典。
>>> Blog.objects.in_bulk([1]) {1: <Blog: Beatles Blog>} >>> Blog.objects.in_bulk([1, 2]) {1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>} >>> Blog.objects.in_bulk([]) {}
若是你給 in_bulk() 傳遞的是一個空列代表,獲得就是一個空字典。
iterator()
運行查詢(QuerySet),而後根據結果返回一個 迭代器(iterator。 作爲比較,使用 QuerySet 時,從數據庫中讀取全部記錄後,一次性將全部記錄實例化爲對應的對象;而 iterator() 則是讀取記錄後,是分屢次對數據實例化,用到哪一個對象才實例化哪一個對象。相對於一次性返回不少對象的 QuerySet,使用迭代器不只效率更高,並且更節省內存。
要注意的是,若是將 iterator() 做用於 QuerySet,那就意味着會再一次運行查詢,就是說會運行兩次查詢。
latest(field_name=None)
根據時間字段 field_name 獲得最新的對象。
下面這個例子根據 pub_date 字段獲得數據表中最新的 Entry 對象:
Entry.objects.latest('pub_date')
若是你在 model 中 Meta 定義了 get_latest_by 項, 那麼你能夠略去 field_name 參數。Django 會將 get_latest_by 作爲默認設置。
和 get(), latest() 同樣,若是根據所給條件沒有找到匹配的對象,就會拋出 DoesNotExist 異常。
注意 latest()和earliest() 是純粹爲了易用易讀而存在的方法。
earliest(field_name=None)
相似於latest()
first()
p = Article.objects.order_by('title', 'pub_date').first()
至關於:
try: p = Article.objects.order_by('title', 'pub_date')[0] except IndexError: p = None
last()
相似於first()
aggregate(*args, **kwargs)
經過對 QuerySet 進行計算,返回一個聚合值的字典。 aggregate() 中每一個參數都指定一個包含在字典中的返回值。
聚合使用關鍵字參數作爲註解的名稱。每一個參數都有一個爲其訂作的名稱,這個名稱取決於聚合函式的函數名和聚合字段的名稱。
例如,你正在處理博文,你想知道博客中一共有多少篇博文:
>>> from django.db.models import Count >>> q = Blog.objects.aggregate(Count('entry')) {'entry__count': 16}
經過在 aggregate 指定關鍵字參數,你能夠控制返回的聚合名稱:
>>> q = Blog.objects.aggregate(number_of_entries=Count('entry')) {'number_of_entries': 16}
exists()
若是 QuerySet 包含有數據,就返回 True 不然就返回 False。這多是最快最簡單的查詢方法了.
update()
更新
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')
delete()
刪除
>>> b = Blog.objects.get(pk=1) # Delete all the entries belonging to this Blog. >>> Entry.objects.filter(blog=b).delete()
字段篩選條件決定了你如何構造 SQL 語句中的 WHERE 從句。它們被指定爲 QuerySet 中 filter(),exclude() 和 get() 方法的關鍵字參數。
exact
精確匹配。若是指定的值是 None,就會翻譯成 SQL 中的 NULL (詳情請查看 isnull )。
例如:
Entry.objects.get(id__exact=14) Entry.objects.get(id__exact=None)
等價的 SQL:
SELECT ... WHERE id = 14; SELECT ... WHERE id IS NULL;
iexact
忽略大小寫的匹配。
例如:
Blog.objects.get(name__iexact='beatles blog') Blog.objects.get(name__iexact=None)
等價於以下 SQL :
SELECT ... WHERE name ILIKE 'beatles blog'; SELECT ... WHERE name IS NULL;
SQLite 用戶要注意
在使用 SQLite 做爲數據庫,而且應用 Unicode (non-ASCII) 字符串時,請先查看 database note 中關於字符串比對那一節內容。SQLite 對 Unicode 字符串,沒法作忽略大小寫的匹配。
contains
大小寫敏感的包含匹配。
例如:
Entry.objects.get(headline__contains='Lennon')
等價於 SQL :
SELECT ... WHERE headline LIKE '%Lennon%';
要注意,上述語句將匹配大標題 'Today Lennon honored' ,但不能匹配 'today lennon honored'。
SQLite 不支持大小寫敏感的 LIKE 語句;因此對 SQLite 使用 contains 時就和使用 icontains 同樣。
icontains
忽略大小寫的包含匹配。
例如:
Entry.objects.get(headline__icontains='Lennon')
等價於 SQL:
SELECT ... WHERE headline ILIKE '%Lennon%';
SQLite 用戶請注意
使用 SQLite 數據庫並應用 Unicode (non-ASCII) 字符串時,請先查看 database note 文檔中關於字符串比對那一節內容。
in
是否在一個給定的列表中。
例如:
Entry.objects.filter(id__in=[1, 3, 4])
等價於 SQL:
SELECT ... WHERE id IN (1, 3, 4);
你也能夠把查詢(queryset)結果當作動態的列表,從而代替固定的列表:
inner_qs = Blog.objects.filter(name__contains='Cheddar') entries = Entry.objects.filter(blog__in=inner_qs)
作動態列表的 queryset 運行時就會被作爲一個子查詢:
SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
若是你傳遞了一個 ValuesQuerySet 或 ValuesListQuerySet (它們是調用查詢集上 values() 和 values_list() 方法的返回結果) 作爲 __in 條件的值,那麼你要確認只匹配返回結果中的一個字段。例如,下面的代碼能正常的工做(對博客名稱進行過濾):
inner_qs = Blog.objects.filter(name__contains='Ch').values('name') entries = Entry.objects.filter(blog__name__in=inner_qs)
下面的代碼卻會拋出異常,緣由是內部的查詢會嘗試匹配兩個字段值,但只有一個是有用的:
# Bad code! Will raise a TypeError. inner_qs = Blog.objects.filter(name__contains='Ch').values('name', 'id') entries = Entry.objects.filter(blog__name__in=inner_qs)
warning
query 屬性本是一個不公開的內部屬性,雖然他在上面的代碼中工做得很好,可是它的API極可能會在不一樣的 Django 版本中常常變更。
性能考慮
要謹慎使用嵌套查詢,而且要對你所採用的數據庫性能有所瞭解(若是不瞭解,就去作一下性能測試)。有些數據庫,好比著名的MySQL,就不能很好地優化嵌套查詢。因此在上面的案例中,先在第一個查詢中提取值列表,而後再將其傳遞給第二個查詢,會對性能有較高的提高。說白了,就是用兩個高效的查詢替換掉一個低效的查詢:
values = Blog.objects.filter( name__contains='Cheddar').values_list('pk', flat=True) entries = Entry.objects.filter(blog__in=list(values))
gt
大於。
gte
大於等於。
lt
小於。
lte
小於等於。
startswith
大小寫敏感的以....開頭。
istartswith
忽略大小寫的以....開頭。
endswith
大小寫敏感的以....結尾。
iendswith
忽略大小寫的以....結尾。
range
包含的範圍。
例如:
import datetime start_date = datetime.date(2005, 1, 1) end_date = datetime.date(2005, 3, 31) Entry.objects.filter(pub_date__range=(start_date, end_date))
等價於 SQL:
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
你能夠把 range 當成 SQL 中的 BETWEEN 來用,好比日期,數字,甚至是字符。
year
對日期/時間字段精確匹配年分,年分用四位數字表示。
例如:
Entry.objects.filter(pub_date__year=2005)
等價於 SQL:
SELECT ... WHERE EXTRACT('year' FROM pub_date) = '2005';
(不一樣的數據庫引擎中,翻譯獲得的 SQL 也不盡相同。)
month
對日期/時間字段精確匹配月分,用整數表示月分,好比 1 表示一月,12 表示十二月。
day
對日期/時間字段精確匹配日期。
要注意的是,這個匹配只會獲得全部 pub_date 字段內容是表示 某月的第三天 的記錄,如一月三號,六月三號。而十月二十三號就不在此列。
week_day
對日期/時間字段匹配星期幾
例如:
Entry.objects.filter(pub_date__week_day=2)
等價於 SQL:
SELECT ... WHERE EXTRACT('dow' FROM pub_date) = '2';
(不一樣的數據庫引擎中,翻譯獲得的 SQL 也不盡相同。)
要注意的是,這段代碼將獲得 pub_date 字段是星期一的全部記錄 (西方習慣於將星期一看作一週的次日),與它的年月信息無關。星期以星期天作爲第一天,以星期六作爲最後一天。
hour
minute
second
isnull
根據 SQL 查詢是空 IS NULL 仍是非空 IS NOT NULL,返回相應的 True 或 False。
例如:
Entry.objects.filter(pub_date__isnull=True)
等價於 SQL:
SELECT ... WHERE pub_date IS NULL;
search
利用全文索引作全文搜索。它與 contains 類似,但使用全文索引作搜索會更快一些。
例如:
Entry.objects.filter(headline__search="+Django -jazz Python")
等價於:
SELECT ... WHERE MATCH(tablename, headline) AGAINST (+Django -jazz Python IN BOOLEAN MODE);
要注意這個方法僅適用於 MySQL ,而且要求設置全文索引。默認狀況下 Django 使用 BOOLEAN MODE 模式。詳見 Please check MySQL documentation for additional details.
regex
大小寫敏感的正則表達式匹配。
它要求數據庫支持正則表達式語法,而 SQLite 卻沒有內建正則表達式支持,所以 SQLite 的這個特性是由一個名爲 REGEXP 的 Python 方法實現的,因此要用到 Python 的正則庫 re.
例如:
Entry.objects.get(title__regex=r'^(An?|The) +')
等價於 SQL:
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'c'); -- Oracle SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite
建議使用原生字符串 (例如,用 r'foo' 替換 'foo') 作爲正則表達式。
iregex
忽略大小寫的正則表達式匹配。
Avg
返回所給字段的平均值。
默認別名:<field>__avg
返回類型: float
Count
根據所給的關聯字段返回被關聯 model 的數量。
默認別名: <field>__count
返回類型: int
它有一個可選參數:
distinct
若是 distinct=True,那麼只返回不重複的實例數量,至關於 SQL 中的 COUNT(DISTINCT field)。默認值是 False。
Max
默認別名: <field>__max
返回類型: 與所給字段值相同
Min
返回所給字段的最小值。
默認別名: <field>__min
返回類型: 與所給字段相同
StdDev
返回所給字段值的標準差。
默認別名: <field>__stddev
返回類型: float
它有一個可選參數:
sample
默認狀況下, StdDev 返回一個整體誤差值,可是若是 sample=True,則返回一個樣本誤差值。
SQLite
SQLite 自己並不提供 StdDev 支持,可使用 SQLite 的外置模塊實現這個功能。詳情請查看相應的 SQLite 文檔,瞭解如何得到和安裝擴展。
Sum
計算所給字段值的總和
默認別名: <field>__sum
返回類型: 與所給字段相同
Variance
返回所給字段值的標準方差。
默認別名: <field>__variance
返回類型: float
它有一個可選參數:
sample默認狀況下, Variance 返回的是整體方差;若是 sample=True,返回的則是樣式方差。