該系列教程系個人原創,並完整發佈在個人官網 "劉江的博客和教程" 所有轉載本文者,需在頂部顯著位置註明原作者及www.liujiangblog.com官網地址。 Python及Django學習QQ群:453131687 以下的方法不會返回QuerySets,但是作用非常強大,尤其是粗體顯示的方法,需 ...
該系列教程系個人原創,並完整發佈在個人官網劉江的博客和教程
所有轉載本文者,需在頂部顯著位置註明原作者及www.liujiangblog.com官網地址。
Python及Django學習QQ群:453131687
以下的方法不會返回QuerySets,但是作用非常強大,尤其是粗體顯示的方法,需要背下來。
方法名 | 解釋 |
---|---|
get() | 獲取單個對象 |
create() | 創建對象,無需save() |
get_or_create() | 查詢對象,如果沒有找到就新建對象 |
update_or_create() | 更新對象,如果沒有找到就創建對象 |
bulk_create() |
批量創建對象 |
count() | 統計對象的個數 |
in_bulk() |
根據主鍵值的列表,批量返回對象 |
iterator() |
獲取包含對象的迭代器 |
latest() | 獲取最近的對象 |
earliest() | 獲取最早的對象 |
first() | 獲取第一個對象 |
last() | 獲取最後一個對象 |
aggregate() | 聚合操作 |
exists() | 判斷queryset中是否有對象 |
update() | 批量更新對象 |
delete() | 批量刪除對象 |
as_manager() | 獲取管理器 |
1. get()
get(**kwargs)
返回按照查詢參數匹配到的單個對象,參數的格式應該符合Field lookups的要求。
如果匹配到的對象個數不只一個的話,觸發MultipleObjectsReturned異常
如果根據給出的參數匹配不到對象的話,觸發DoesNotExist異常。例如:
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.")
如果希望查詢器只返回一行,則可以使用get()而不使用任何參數來返回該行的對象:
entry = Entry.objects.filter(...).exclude(...).get()
2. create()
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中有一個你手動設置的主鍵,並且這個值已經存在於資料庫中, 調用create()將會失敗並且觸發IntegrityError因為主鍵必須是唯一的。如果你手動設置了主鍵,做好異常處理的準備。
3. get_or_create()
get_or_create(defaults=None, **kwargs)
通過kwargs來查詢對象的便捷方法(如果模型中的所有欄位都有預設值,可以為空),如果該對象不存在則創建一個新對象。
該方法返回一個由(object, created)組成的元組,元組中的object 是一個查詢到的或者是被創建的對象, created是一個表示是否創建了新的對象的布爾值。
對於下麵的代碼:
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()
如果模型的欄位數量較大的話,這種模式就變的非常不易用了。 上面的示例可以用get_or_create()
重寫 :
obj, created = Person.objects.get_or_create(
first_name='John',
last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)},
)
任何傳遞給get_or_create()
的關鍵字參數,除了一個可選的defaults,都將傳遞給get()調用。 如果查找到一個對象,返回一個包含匹配到的對象以及False 組成的元組。 如果查找到的對象超過一個以上,將引發MultipleObjectsReturned。如果查找不到對象,get_or_create()
將會實例化並保存一個新的對象,返回一個由新的對象以及True組成的元組。新的對象將會按照以下的邏輯創建:
params = {k: v for k, v in kwargs.items() if '__' not in k}
params.update({k: v() if callable(v) else v for k, v in defaults.items()})
obj = self.model(**params)
obj.save()
它表示從非'defaults' 且不包含雙下劃線的關鍵字參數開始。然後將defaults的內容添加進來,覆蓋必要的鍵,並使用結果作為關鍵字參數傳遞給模型類。
如果有一個名為defaults__exact
的欄位,並且想在get_or_create()
時用它作為精確查詢,只需要使用defaults,像這樣:
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
當你使用手動指定的主鍵時,get_or_create()
方法與create()
方法有相似的錯誤行為 。 如果需要創建一個對象而該對象的主鍵早已存在於資料庫中,IntegrityError異常將會被觸發。
這個方法假設進行的是原子操作,並且正確地配置了資料庫和正確的底層資料庫行為。如果資料庫級別沒有對get_or_create
中用到的kwargs強制要求唯一性(unique和unique_together),方法容易導致競態條件,可能會有相同參數的多行同時插入。(簡單理解,kwargs必須指定的是主鍵或者unique屬性的欄位才安全。)
最後建議只在Django視圖的POST請求中使用get_or_create(),因為這是一個具有修改性質的動作,不應該使用在GET請求中,那樣不安全。
可以通過ManyToManyField屬性和反向關聯使用get_or_create()
。在這種情況下,應該限制查詢在關聯的上下文內部。 否則,可能導致完整性問題。
例如下麵的模型:
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的chapters欄位使用get_or_create()
,但是它只會獲取該Book內部的上下文:
>>> 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 “Ulysses”獲取或者創建“Chapter 1”,但是它不能,因為它與這個book不關聯,但因為title 欄位是唯一的它仍然不能創建。
在Django1.11在defaults中增加了對可調用值的支持。
4. update_or_create()
update_or_create(defaults=None, **kwargs)
類似前面的get_or_create()
。
通過給出的kwargs來更新對象的便捷方法, 如果沒找到對象,則創建一個新的對象。defaults是一個由 (field, value)對組成的字典,用於更新對象。defaults中的值可以是可調用對象(也就是說函數等)。
該方法返回一個由(object, created)組成的元組,元組中的object是一個創建的或者是被更新的對象, created是一個標示是否創建了新的對象的布爾值。
update_or_create
方法嘗試通過給出的kwargs 去從資料庫中獲取匹配的對象。 如果找到匹配的對象,它將會依據defaults 字典給出的值更新欄位。
像下麵的代碼:
defaults = {'first_name': 'Bob'}
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
for key, value in defaults.items():
setattr(obj, key, value)
obj.save()
except Person.DoesNotExist:
new_values = {'first_name': 'John', 'last_name': 'Lennon'}
new_values.update(defaults)
obj = Person(**new_values)
obj.save()
如果模型的欄位數量較大的話,這種模式就變的非常不易用了。 上面的示例可以用update_or_create()
重寫:
obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon',
defaults={'first_name': 'Bob'},
)
kwargs中的名稱如何解析的詳細描述可以參見get_or_create()
。
和get_or_create()
一樣,這個方法也容易導致競態條件,如果資料庫層級沒有前置唯一性會讓多行同時插入。
在Django1.11在defaults中增加了對可調用值的支持。
5. bulk_create()
bulk_create(objs, batch_size=None)
以高效的方式(通常只有1個查詢,無論有多少對象)將提供的對象列表插入到資料庫中:
>>> Entry.objects.bulk_create([
... Entry(headline='This is a test'),
... Entry(headline='This is only a test'),
... ])
註意事項:
- 不會調用模型的save()方法,並且不會發送
pre_save
和post_save
信號。 - 不適用於多表繼承場景中的子模型。
- 如果模型的主鍵是AutoField,則不會像save()那樣檢索並設置主鍵屬性,除非資料庫後端支持。
- 不適用於多對多關係。
batch_size
參數控制在單個查詢中創建的對象數。
6. count()
count()
返回在資料庫中對應的QuerySet對象的個數。count()永遠不會引發異常。
例如:
# 返回總個數.
Entry.objects.count()
# 返回包含有'Lennon'的對象的總數
Entry.objects.filter(headline__contains='Lennon').count()
7. in_bulk()
in_bulk(id_list=None)
獲取主鍵值的列表,並返回將每個主鍵值映射到具有給定ID的對象的實例的字典。 如果未提供列表,則會返回查詢集中的所有對象。
例如:
>>> 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([])
{}
>>> Blog.objects.in_bulk()
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
如果向in_bulk()
傳遞一個空列表,會得到一個空的字典。
在舊版本中,id_list
是必需的參數,現在是一個可選參數。
8. iterator()
iterator()
提交資料庫操作,獲取QuerySet,並返回一個迭代器。
QuerySet通常會在內部緩存其結果,以便在重覆計算時不會導致額外的查詢。而iterator()將直接讀取結果,不在QuerySet級別執行任何緩存。對於返回大量只需要訪問一次的對象的QuerySet,這可以帶來更好的性能,顯著減少記憶體使用。
請註意,在已經提交了的iterator()上使用QuerySet會強制它再次提交資料庫操作,進行重覆查詢。此外,使用iterator()會導致先前的prefetch_related()
調用被忽略,因為這兩個一起優化沒有意義。
9. latest()
latest(field_name=None)
使用日期欄位field_name,按日期返回最新對象。
下例根據Entry的'pub_date'欄位返回最新發佈的entry:
Entry.objects.latest('pub_date')
如果模型的Meta指定了get_latest_by
,則可以將latest()參數留給earliest()或者field_name
。 預設情況下,Django將使用get_latest_by
中指定的欄位。
earliest()和latest()可能會返回空日期的實例,可能需要過濾掉空值:
Entry.objects.filter(pub_date__isnull=False).latest('pub_date')
10. earliest()
earliest(field_name=None)
類同latest()。
11. first()
first()
返回結果集的第一個對象, 當沒有找到時返回None。如果QuerySet沒有設置排序,則將會自動按主鍵進行排序。例如:
p = Article.objects.order_by('title', 'pub_date').first()
first()是一個簡便方法,下麵的例子和上面的代碼效果是一樣:
try:
p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
p = None
12. last()
last()
工作方式類似first(),只是返回的是查詢集中最後一個對象。
13. aggregate()
aggregate(*args, **kwargs)
返回彙總值的字典(平均值,總和等),通過QuerySet進行計算。每個參數指定返回的字典中將要包含的值。
使用關鍵字參數指定的聚合將使用關鍵字參數的名稱作為Annotation 的名稱。 匿名參數的名稱將基於聚合函數的名稱和模型欄位生成。 複雜的聚合不可以使用匿名參數,必須指定一個關鍵字參數作為別名。
例如,想知道Blog Entry 的數目:
>>> from django.db.models import Count
>>> q = Blog.objects.aggregate(Count('entry'))
{'entry__count': 16}
通過使用關鍵字參數來指定聚合函數,可以控制返回的聚合的值的名稱:
>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))
{'number_of_entries': 16}
14. exists()
exists()
如果QuerySet包含任何結果,則返回True,否則返回False。
查找具有唯一性欄位(例如primary_key)的模型是否在一個QuerySet中的最高效的方法是:
entry = Entry.objects.get(pk=123)
if some_queryset.filter(pk=entry.pk).exists():
print("Entry contained in queryset")
它將比下麵的方法快很多,這個方法要求對QuerySet求值並迭代整個QuerySet:
if entry in some_queryset:
print("Entry contained in QuerySet")
若要查找一個QuerySet是否包含任何元素:
if some_queryset.exists():
print("There is at least one object in some_queryset")
將快於:
if some_queryset:
print("There is at least one object in some_queryset")
15. update()
update(**kwargs)
對指定的欄位執行批量更新操作,並返回匹配的行數(如果某些行已具有新值,則可能不等於已更新的行數)。
例如,要對2010年發佈的所有博客條目啟用評論,可以執行以下操作:
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
可以同時更新多個欄位 (沒有多少欄位的限制)。 例如同時更新comments_on和headline欄位:
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')
update()方法無需save操作。唯一限制是它只能更新模型主表中的列,而不是關聯的模型,例如不能這樣做:
>>> Entry.objects.update(blog__name='foo') # Won't work!
仍然可以根據相關欄位進行過濾:
>>> Entry.objects.filter(blog__id=1).update(comments_on=True)
update()方法返回受影響的行數:
>>> Entry.objects.filter(id=64).update(comments_on=True)
1
>>> Entry.objects.filter(slug='nonexistent-slug').update(comments_on=True)
0
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
132
如果你只是更新一下對象,不需要為對象做別的事情,最有效的方法是調用update(),而不是將模型對象載入到記憶體中。 例如,不要這樣做:
e = Entry.objects.get(id=10)
e.comments_on = False
e.save()
建議如下操作:
Entry.objects.filter(id=10).update(comments_on=False)
用update()還可以防止在載入對象和調用save()之間的短時間內資料庫中某些內容可能發生更改的競爭條件。
如果想更新一個具有自定義save()方法的模型的記錄,請迴圈遍歷它們並調用save(),如下所示:
for e in Entry.objects.filter(pub_date__year=2010):
e.comments_on = False
e.save()
16. delete()
delete()
批量刪除QuerySet中的所有對象,並返回刪除的對象個數和每個對象類型的刪除次數的字典。
delete()動作是立即執行的。
不能在QuerySet上調用delete()。
例如,要刪除特定博客中的所有條目:
>>> b = Blog.objects.get(pk=1)
# Delete all the entries belonging to this Blog.
>>> Entry.objects.filter(blog=b).delete()
(4, {'weblog.Entry': 2, 'weblog.Entry_authors': 2})
預設情況下,Django的ForeignKey使用SQL約束ON DELETE CASCADE,任何具有指向要刪除的對象的外鍵的對象將與它們一起被刪除。 像這樣:
>>> blogs = Blog.objects.all()
# This will delete all Blogs and all of their Entry objects.
>>> blogs.delete()
(5, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.Entry_authors': 2})
這種級聯的行為可以通過的ForeignKey的on_delete參數自定義。(什麼時候要改變這種行為呢?比如日誌數據,就不能和它關聯的主體一併被刪除!)
delete()會為所有已刪除的對象(包括級聯刪除)發出pre_delete
和post_delete
信號。
17. as_manager()
classmethod as_manager()
一個類方法,返回Manager的實例與QuerySet的方法的副本。