默认情况下Django 管理员, 丢失更新 or 写倾斜引起的竞争条件可能会发生,因为select_for_update()
未使用。 *我的答案 https://stackoverflow.com/questions/27826714/lost-update-vs-write-skew/73847878#73847878解释丢失更新 and 写倾斜.
所以,我用以下代码编写了示例代码select_for_update()
阻止丢失更新 or 写倾斜 in Django 管理员如下所示。 *我用了姜戈 3.2.16 and PostgreSQL:
<丢失更新>
例如,您创建store_product
table with id
, name
and stock
with models.py
如下所示:
store_product
table:
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()
然后,您需要覆盖获取查询集() https://github.com/django/django/blob/main/django/contrib/admin/options.py#L412 with select_for_update()
in ProductAdmin():
如下所示:
# "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
然后,如果你改变(更新)product如下所示:
SELECT FOR UPDATE
and UPDATE
queries正在运行交易根据PostgreSQL 查询日志如下所示。 *您可以检查如何记录 PostgreSQL 查询 https://stackoverflow.com/questions/54780698/postgresql-database-log-transaction/73432601#73432601:
而且,如果你不重写get_queryset()
in 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运行如下图所示:
<写入倾斜>
例如,您创建store_doctor
table with id
, name
and on_call
with models.py
如下所示:
store_doctor
table:
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()
然后,您需要覆盖响应_更改() https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.response_change with select_for_update()
and 保存模型() https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_model in DoctorAdmin():
如下所示。 *至少一名医生必须待命:
# "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()
然后,如果你改变(更新)doctor如下所示:
SELECT FOR UPDATE
and UPDATE
queries正在运行交易但我不知道如何删除the 1st SELECT
query浅蓝色如下图:
而且,如果你不重写response_change()
and save_model()
in 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运行如下图所示:
例如对于write skew
再次,你创造了store_event
table with id
, name
and user
with models.py
如下所示:
store_event
table:
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)
然后,您需要覆盖response_add()
with select_for_update()
and save_model()
in EventAdmin():
如下所示。 *只有3位用户可以加入“制作寿司”活动:
# "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()
然后,如果你添加event如下所示:
SELECT FOR UPDATE
and INSERT
queries正在运行交易如下所示:
而且,如果你不重写响应_添加() https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.response_add and save_model()
in EventAdmin():
如下所示:
# "store/admin.py"
from django.contrib import admin
from .models import Event
@admin.register(Event)
class EventAdmin(admin.ModelAdmin):
pass
Only INSERT
query运行如下图所示:
您还可以查看我下面的帖子选择更新在姜戈中:
-
在 Django Admin 中添加数据时如何运行“SELECT FOR UPDATE”而不是“SELECT”? https://stackoverflow.com/questions/74831244/how-to-run-select-for-update-instead-of-select-when-adding-data-in-django-ad
-
在 Django Admin 中更改和删除数据时,如何运行“SELECT FOR UPDATE”而不是“SELECT”? https://stackoverflow.com/questions/74807372/how-to-run-select-for-update-instead-of-select-when-changing-and-deleting-da
-
如何在 Django 管理操作中为默认“删除所选”运行“SELECT FOR UPDATE”? https://stackoverflow.com/questions/74857394/how-to-run-select-for-update-for-the-default-delete-selected-in-django-admin