Django筆記十三之select_for_update等選擇和更新等相關操作

XHunter發表於2023-04-01

本篇筆記將介紹 update 和 create 的一些其他用法,目錄如下:

  1. get_or_create
  2. update_or_create
  3. select_for_update
  4. bulk_create
  5. bulk_update

1、get_or_create

前面我們介紹過 get() 和 create() 的用法,那麼 get_or_create() 的意思很簡單,就是 獲取或者建立,如果存在就返回,不存在就先建立再返回

假設對於 Blog model,我們想看下資料庫有沒有 name="hunter", tagline="tagline_test" 的資料,沒有的話建立並獲取這條資料,有的話,就直接獲取。

在之前我們操作可能是:

try:
	blog = Blog.objects.get(name='hunter', tagline='tagline_test')
except Blog.DoesNotExist:
	blog = Blog(name='hunter', tagline='tagline_test')
	blog.save()		

現在我們可以直接這樣操作:

blog, created = Blog.objects.get_or_create(name='hunter', tagline='tagline_test')

這個函式的返回值有兩個,一個是操作的 model 例項,一個是是否是 created 的 布林型資料。

created 為 True,表示這條資料是建立,create() 到的
created 為 False,表示這條資料是獲取, get() 到的

注意: 查詢的條件必須是唯一的,否則會造成多條資料返回而報錯,這個邏輯同 get() 函式。

注意: 使用的欄位,沒有唯一的約束,併發的呼叫這個方法可能會導致多條相同的值插入。

欄位預設值

假設 Blog 這個 model 除了 name, tagline 這兩個欄位外,還有 field_1 和 field_2 欄位,但是他們不在我們查詢的條件內,作用為在建立的時候設定的預設值,我們可以透過 defaults 來操作:

blog, created = Blog.objects.get_or_create(
	name='hunter', 
	tagline='tagline_test',
	defaults={
		'field_1': 'field_1_value',
		'field_2': 'field_2_value'
	}
)

最後關於這個函式,有個小提示,如果這個函式用在介面裡,那麼根據冪等性,我們應該使用 POST 方法來請求,而不是 GET 請求。

關於冪等性的概念,有興趣的話可以去查詢一下。

2、update_or_create

更新或者建立,使用方法同 get_or_create()

假設對於 Blog model 我們想實現的操作如果存在 name='hunter', tagline='tagline_test' 的資料就將其 field_1 和 field_2 的欄位更新,不存在的話,就建立該資料。

之前的操作邏輯大概如下:

defaults = {"field_1": "field_1_value", "field_2": "field_2_value"}

try:
	obj = Blog.objects.get(name='hunter', tagline='tagline_test')
	for key, value in defaults.items():
		setattr(obj, key, value)
	obj.save()
except:
	new_values = {"name": "hunter", "tagline": "tagline_test}
	new_values.update(defaults)
	obj = Blog(**new_values)
	obj.save()

現在我們使用 update_or_create 可以如下操作:

obj, created = Blog.objects.update_or_create(
	name='hunter', tagline='tagline_test',
	defaults={"field_1": "field_1_value", "field_2": "field_2_value"}
)

3、select_for_update

select_for_update 的操作複雜一點,作用類似於 SQL 中的 SELECT ... FOR UPDATE 語句

操作如下:

from django.db import transaction

blog_list = Blog.objects.select_for_update().filter(name="hunter")
with transaction.atomic():
	for blog in blog_list:
		...

當 blog_list 去獲取資料的時候,所有匹配上的 entries 都會被鎖,直到這個事務結束。

意味著這個時候,其他的事務會被阻止更改或者重新在這些資料上加鎖。

我們來舉個例子,在我們執行下面的語句時:

import time
from django.db import transaction

blog_list = Blog.objects.select_for_update().filter(name="hunter")
with transaction.atomic():
	for blog in blog_list:
		print("locking ...")
		time.sleep(20)

這個時候,我們在重新開一個 shell,來執行下面的語句:

Blog.objects.filter(name="hunter").update(name="hunter_1")

因為第一個 shell 裡執行的命令還沒有結束,而且在資料上加了鎖,因此第二個 shell 裡的語句會進入等待,直到第一個 shell 裡的命令執行完成之後,第二個 shell 裡的命令才會執行。

注意: 如果在第一個命令裡,對 blog 資料進行操作,比如 把 name 欄位改為了 hunter_2,那麼在第二條命令的條件裡篩選不到結果然後更新的。

4、bulk_create

批次建立,在前面介紹增刪改查的時候介紹過一次,這裡再簡單做一下示例:

from blog.models import Blog

blog_list = [
	Blog(name="hunter_1", tagline="tag_1"),
	Blog(name="hunter_2", tagline="tag_2"),
	Blog(name="hunter_3", tagline="tag_3"),
	Blog(name="hunter_4", tagline="tag_4")
]

Blog.objects.bulk_create(blog_list)

如果我們批次建立的數量過多,我們可以指定分批次來建立,透過 batch_size 引數來指定。

Blog.objects.bulk_create(blog_list, batch_size=2)

5、bulk_update

批次更新,方式與 bulk_create 的方式類似,以下是使用示例:

blog_list = Blog.objects.filter(id__lte=20)

for blog in blog_list:
	blog.name = "name_updated"
	blog.tagline = "tag_updated"

Blog.objects.bulk_update(blog_list, fields=['name'], batch_size=2)

需要注意的是 bulk_update 多了個引數,fields 這個是用來指定需要更新的欄位。

如我們上面的命令所示,我們指定更新的是 name 欄位,那麼就算我們更改了 tagline 的資料,只要 fields 列表裡沒有指定該欄位,那麼後臺也不會更新該欄位。

以上就是本篇筆記全部內容,接下來我們將介紹一下查詢中的其他用法,比如latest,first,contains 等。

本文首發於本人微信公眾號:Hunter後端

原文連結:Django筆記十三之select_for_update等選擇和更新等相關操作

如果想獲取更多相關文章,可掃碼關注閱讀:
image

相關文章