我遇到了名为
projects/
的 Django Rest Framework ModelViewSet 端点的问题。我有一组请求(PATCH、DELETE、然后 GET)导致意外行为。请求和响应的时间表如下:
此行为表明存在潜在的竞争条件或缓存问题,其中 PATCH 操作在 DELETE 之后完成,或者 GET 请求返回不反映删除的缓存列表。
视图、序列化器和模型代码都非常普通:
class ProjectViewSet(ModelViewSet):
parser_classes = (MultiPartParser, FormParser, JSONParser)
queryset = Project.objects.all()
serializer_class = ProjectSerializer
pagination_class = ProjectPagination
class ProjectSerializer(serializers.ModelSerializer):
creator = UserUUIDField(default=serializers.CurrentUserDefault())
image = serializers.ImageField(required=False)
class Meta:
model = Project
fields = "__all__"
class Project(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
creator = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
image = models.ForeignKey(
wand_image,
on_delete=models.DO_NOTHING,
null=True,
blank=True,
related_name="projects"
)
一个模型有对该模型的外键引用,但 on_delete 行为是将其设置为 null。
我正在使用 Google Cloud Run(一项无服务器后端服务)运行此程序
Rest Framework mixins 不是原子操作,因此有必要使用自定义 mixins 进行 UPDATE 和 DELETE 操作:
class AtomicDestroyModelMixin:
"""
Destroy a model instance.
"""
def destroy(self, request, *args, **kwargs):
try:
with transaction.atomic():
instance = self.get_object()
self.perform_destroy(instance)
except OperationalError:
raise DatabaseOperationException()
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
class AtomicUpdateModelMixin:
"""
Update a model instance.
"""
def update(self, request, *args, **kwargs):
try:
with transaction.atomic():
partial = kwargs.pop("partial", False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, "_prefetched_objects_cache", None):
instance._prefetched_objects_cache = {}
except OperationalError:
raise DatabaseOperationException()
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
def partial_update(self, request, *args, **kwargs):
kwargs["partial"] = True
return self.update(request, *args, **kwargs)