Django中不返回QuerySets的API -- Django從入門到精通系列教程

該系列教程系我的原創,並完整發布在我的官網劉江的博客和教程

全部轉載本文者,需在頂部顯著位置註明原做者及www.liujiangblog.com官網地址。


如下的方法不會返回QuerySets,可是做用很是強大,尤爲是粗體顯示的方法,須要背下來。python

方法名 解釋
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)web

返回按照查詢參數匹配到的單個對象,參數的格式應該符合Field lookups的要求。數據庫

若是匹配到的對象個數不僅一個的話,觸發MultipleObjectsReturned異常django

若是根據給出的參數匹配不到對象的話,觸發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)函數

在一步操做中同時建立而且保存對象的便捷方法.post

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_savepost_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_deletepost_delete信號。

17. as_manager()

classmethod as_manager()

一個類方法,返回Manager的實例與QuerySet的方法的副本。

相關文章
相關標籤/搜索