Django Admin中是否存在竞态条件?如何预防?
Django Admin 竞态条件相关问题解答
1. Django Admin 是否存在竞态条件?有哪些预防方法?
Django Admin 默认的编辑流程存在竞态条件风险(比如丢失更新、写偏斜)。因为默认逻辑是先从数据库读取对象数据,在页面编辑后提交保存,这个过程中如果有其他用户或请求同时修改同一对象,后提交的更新会覆盖先提交的变更,导致数据丢失。
常见的预防方法包括:
- 悲观锁(
select_for_update()):在读取对象时加行级锁,阻止其他事务修改该对象直到当前事务完成。 - 乐观锁:给模型添加版本字段(如
version = models.IntegerField(default=1)),保存时检查版本号是否匹配,若不匹配则拒绝更新并提示冲突。 - 事务包裹逻辑:在Admin的保存流程中用事务包裹读取和更新操作,结合锁机制确保原子性。
2. Django Admin 默认是否使用 select_for_update()?如何查看/自定义实现?
Django Admin 默认没有使用select_for_update()来预防竞态条件。你可以查看Django Admin的源码确认:
在django.contrib.admin.options.ModelAdmin类的get_object方法中,默认直接通过模型管理器获取对象,核心代码如下:
def get_object(self, request, object_id, from_field=None): queryset = self.get_queryset(request) model = queryset.model field = model._meta.get_field(from_field or self.pk_name) try: object_id = field.to_python(object_id) return queryset.get(**{field.name: object_id}) except (model.DoesNotExist, ValueError, TypeError): return None
如果要在Admin中实现select_for_update(),可以重写get_object方法,在获取对象时添加锁:
from django.contrib import admin from .models import YourModel class YourModelAdmin(admin.ModelAdmin): def get_object(self, request, object_id, from_field=None): # 添加select_for_update()加行级锁 queryset = self.get_queryset(request).select_for_update() model = queryset.model field = model._meta.get_field(from_field or self.pk_name) try: object_id = field.to_python(object_id) return queryset.get(**{field.name: object_id}) except (model.DoesNotExist, ValueError, TypeError): return None
这样进入编辑页面时,会对目标对象加行级锁,直到当前请求的事务完成,避免其他请求同时修改该对象。
注意:Django Admin的默认视图已经包裹了事务,因此上述自定义方法可以直接生效。
内容的提问来源于stack exchange,提问作者Super Kai - Kazuya Ito
相关产品推荐
相关产品推荐

