[英]Does race condition(lost update or write skew) happen in Django admin?
In Django views , we can use select_for_update()
to prevent race condition(lost update or write skew) so race condition doesn't happen in Django views with select_for_update()
.在Django 视图中,我们可以使用
select_for_update()
来防止竞争条件(丢失更新或写入倾斜) ,因此在Django 视图中使用select_for_update()
不会发生竞争条件。 *I used Django 3.2.16 . *我用的是 Django 3.2.16 。
But, even though I googled, I couldn't find any information saying "in Django admin , race condition doesn't happen or select_for_update()
is used to prevent race condition " .但是,即使我用谷歌搜索,我也找不到任何信息说“在Django admin中,竞争条件不会发生或
select_for_update()
用于防止竞争条件” 。
So, in Django admin , does race condition happen?那么,在Django admin中,竞争条件会发生吗?
If yes, are there any ways to prevent race condition in Django admin ?如果是,是否有任何方法可以防止Django admin中的竞争条件?
If no, is select_for_update()
or other way used to prevent race condition in Django admin ?如果不是,是否使用
select_for_update()
或其他方式来防止Django admin中的竞争条件? and can I see the code for me?我可以看到我的代码吗?
By default in Django Admin , lost update or write skew caused by race condition happens because select_for_update()
is not used.默认情况下,在Django Admin中,由于未使用
select_for_update()
,会发生由于竞争条件导致的丢失更新或写入倾斜。 * My answer explains lost update and write skew . * 我的回答解释了 lost update和write skew 。
So, I wrote the example code with select_for_update()
to prevent lost update or write skew in Django Admin as shown below.因此,我使用
select_for_update()
编写了示例代码,以防止丢失更新或在Django Admin中写入倾斜,如下所示。 *I used Django 3.2.16 and PostgreSQL : *我使用了 Django 3.2.16和PostgreSQL :
For example, you create store_product
table with id
, name
and stock
with models.py
as shown below:例如,您使用
models.py
创建带有id
、 name
和stock
的store_product
表,如下所示:
store_product
table: store_product
表: id ![]() |
name![]() |
stock![]() |
---|---|---|
1 ![]() |
Apple![]() |
10 ![]() |
2 ![]() |
Orange![]() |
20 ![]() |
# "store/models.py"
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=30)
stock = models.IntegerField()
Then, you need to override get_queryset() with select_for_update()
in ProductAdmin():
as shown below:然后,您需要在
ProductAdmin():
中用select_for_update()
覆盖get_queryset() ,如下所示:
# "store/admin.py"
from django.contrib import admin
from .models import Product
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super().get_queryset(request)
last_part_of_referer = request.META.get('HTTP_REFERER').split('/')[-2]
last_part_of_uri = request.build_absolute_uri().split('/')[-2]
if (last_part_of_referer == "change" and last_part_of_uri == "change"):
qs = qs.select_for_update()
return qs
Then, if you change(update) product as shown below:然后,如果您更改(更新)产品,如下所示:
SELECT FOR UPDATE
and UPDATE
queries are run in transaction according to the PostgreSQL query logs as shown below. SELECT FOR UPDATE
和UPDATE
查询根据PostgreSQL 查询日志在事务中运行,如下所示。 *You can check how to log PostgreSQL queries : *您可以查看如何记录 PostgreSQL 查询:
And, if you don't override get_queryset()
in ProductAdmin():
as shown below:而且,如果您不覆盖 ProductAdmin(
get_queryset()
ProductAdmin():
如下所示:
# "store/admin.py"
from django.contrib import admin
from .models import Product
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
pass
SELECT
and UPDATE
queries are run as shown below: SELECT
和UPDATE
查询运行如下所示:
For example, you create store_doctor
table with id
, name
and on_call
with models.py
as shown below:例如,您使用
models.py
创建带有id
、 name
和on_call
的store_doctor
表,如下所示:
store_doctor
table: store_doctor
表: id ![]() |
name![]() |
on_call![]() |
---|---|---|
1 ![]() |
John![]() |
True![]() |
2 ![]() |
Lisa![]() |
True![]() |
# "store/models.py"
from django.db import models
class Doctor(models.Model):
name = models.CharField(max_length=30)
on_call = models.BooleanField()
Then, you need to override response_change()
with select_for_update()
and save_model()
in DoctorAdmin():
as shown below:然后,您需要在
DoctorAdmin():
中用select_for_update()
和save_model()
覆盖response_change()
,如下所示:
# "store/admin.py"
from django.contrib import admin
from .models import Doctor
from django.db import connection
@admin.register(Doctor)
class DoctorAdmin(admin.ModelAdmin):
def response_change(self, request, obj):
qs = super().get_queryset(request).select_for_update().filter(on_call=True)
obj_length = len(qs)
if obj_length == 0:
obj.on_call = True
obj.save()
return super().response_change(request, obj)
def save_model(self, request, obj, form, change):
last_part_of_path = request.path.split('/')[-2]
if last_part_of_path == "add":
obj.save()
Then, if you change(update) doctor as shown below:然后,如果您更改(更新)医生,如下所示:
SELECT FOR UPDATE
and UPDATE
queries are run in transaction but I don't know how to remove the 1st SELECT
query in light blue as shown below: SELECT FOR UPDATE
和UPDATE
查询在事务中运行,但我不知道如何删除浅蓝色的第一个SELECT
查询,如下所示:
And, if you don't override response_change()
and save_model()
in DoctorAdmin():
as shown below:并且,如果您不覆盖 DoctorAdmin() 中的
response_change()
和save_model()
DoctorAdmin():
如下所示:
# "store/admin.py"
from django.contrib import admin
from .models import Doctor
@admin.register(Doctor)
class DoctorAdmin(admin.ModelAdmin):
pass
SELECT
and UPDATE
queries are run as shown below: SELECT
和UPDATE
查询运行如下所示:
For example for write skew
again, you create store_event
table with id
, name
and user
with models.py
as shown below:例如,再次
write skew
,您使用models.py
创建带有id
、 name
和user
的store_event
表,如下所示:
store_event
table: store_event
表: id ![]() |
name![]() |
user![]() |
---|---|---|
1 ![]() |
Make Sushi![]() |
John![]() |
2 ![]() |
Make Sushi![]() |
Tom![]() |
# "store/models.py"
from django.db import models
class Event(models.Model):
name = models.CharField(max_length=30)
user = models.CharField(max_length=30)
Then, you need to override response_add()
with select_for_update()
and save_model()
in EventAdmin():
as shown below:然后,您需要在
EventAdmin():
中用select_for_update()
和save_model()
覆盖response_add()
,如下所示:
# "store/admin.py"
from django.contrib import admin
from .models import Event
from django.db import connection
@admin.register(Event)
class EventAdmin(admin.ModelAdmin):
def response_add(self, request, obj, post_url_continue=None):
qs = super().get_queryset(request).select_for_update().filter(name="Make Sushi")
obj_length = len(qs)
if obj_length < 3:
obj.save()
return super().response_add(request, obj, post_url_continue)
def save_model(self, request, obj, form, change):
last_part_of_path = request.path.split('/')[-2]
if last_part_of_path == "change":
obj.save()
Then, if you add event as shown below:然后,如果您添加事件,如下所示:
SELECT FOR UPDATE
and INSERT
queries are run in transaction as shown below: SELECT FOR UPDATE
和INSERT
查询在事务中运行,如下所示:
And, if you don't override response_add()
and save_model()
in EventAdmin():
as shown below:并且,如果您不重写 EventAdmin() 中的
response_add()
和save_model()
EventAdmin():
如下所示:
# "store/admin.py"
from django.contrib import admin
from .models import Event
@admin.register(Event)
class EventAdmin(admin.ModelAdmin):
pass
Only INSERT
query is run as shown below:仅运行
INSERT
查询,如下所示:
Yes, a race condition can occur in Django admin if multiple users try to update the same object at the same time.是的,如果多个用户尝试同时更新同一个 object,Django admin 中可能会出现竞争条件。 This can result in lost updates or write skew, where the final value of the object is not what was intended.
这可能会导致更新丢失或写入偏斜,其中 object 的最终值不是预期的值。 To avoid this, Django admin uses optimistic locking to ensure that updates are performed safely and consistently.
为避免这种情况,Django 管理员使用乐观锁定来确保安全且一致地执行更新。 This means that if two users try to update the same object simultaneously, one of the updates will be rejected and the user will need to retrieve the latest version of the object and try again.
这意味着如果两个用户尝试同时更新同一个 object,其中一个更新将被拒绝,用户将需要检索 object 的最新版本并重试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.