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

来源:https://www.cnblogs.com/feixuelove1009/archive/2018/02/07/8425054.html
-Advertisement-
Play Games

該系列教程系個人原創,並完整發佈在個人官網 "劉江的博客和教程" 所有轉載本文者,需在頂部顯著位置註明原作者及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_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的方法的副本。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、標簽分類 1.1 自閉和標簽 自閉和標簽只有開頭沒有結尾,自動閉合: 1.2主動閉合標簽 有開頭也有結尾,是主動閉合的,稱為主動閉合標簽,我們用到的大部分都是主動閉合標簽 二、Head標簽 2.1 meta 標簽 <meta>元素可提供有關頁面的元信息(meta-information),針對搜 ...
  • 方法一: div(父):display:table; div(子):display:table_cell;margin:0 auto;vertical-align:middle; 方法二: div(父):display:flex;justify-context:centet;align-items: ...
  • (一)水平對齊1.使用margin屬性水平對齊可通過將左和右外邊距設置為 "auto",來對齊塊元素。除非已經聲明瞭 !DOCTYPE,否則使用 margin:auto 在 IE8 以及更早的版本中是無效的。 如果寬度是 100%,則對齊沒有效果。 2.使用 position 屬性進行左和右對齊對齊 ...
  • 將數據轉換為 JavaScript 對象:JSON.parse();將 JavaScript 對象轉換為字元串:JSON.stringify(); ...
  • jQuery Ajax的使用場景: 頁面需要通過後臺邏輯,但只需要局部刷新以顯示新的內容。 jQuery Ajax url使用方式1.servlet方式: 需要在struts.xml中寫一個action,跳轉地址寫servlet; 需要重寫init方法(為了使用spring註入的bean); 設置響 ...
  • source from http://tutorials.jenkov.com/java-concurrency/threadlocal.html#inheritableThreadLocal Java ThreadLocal Creating a ThreadLocal Accessing a T ...
  • 作用: 裝飾者設計模式的作用是使被裝飾者的功能更強大,比如一把ak47裝上消聲器,裝上瞄準鏡,槍的使用方式不變,改變的是這把槍的功能更加強大,功能更多。 舉例1: 女人可以通過化妝、受教育、禮貌 變身成化妝的女人(顏值+)、高智商的女人(IQ+)、有禮貌的女人(禮貌用語用的更多) 註意:為了顯示出視 ...
  • 因為最近項目正在做重構,而這次重構實質上比原來更接近於SOA化和微服務的思想。對於我們金融交易來說,數據結果的準確性是重中之重。所以今天總結一下分散式事務的實現方法,下次組內周會給大家統一一下概念。 剛性事務和柔性事務 剛性事務:嚴格遵循ACID原則(原子性、一致性、隔離性、持久性)的事務。基本上指 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...