我知道这个问题已经被问过很多次了,但是我仍然找不到正确的解决方案。,比如说我有像Follow这样的模型
class Student(models.Model):
number = models.IntegerField()
department = models.ForeignKey(Department, on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(fields=['department', 'number'])
]
和我的序列化器如下所示。
class StudentModelSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = ("number",)
在此模型department
中,number
为unique together
,现在从URL中传递的pk
中提取部门。我处理唯一错误的方式如下。
class StudentViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def perform_create(self, serializer):
department = Department.objects.get(pk=self.kwargs['pk'])
serializer.save(department=department)
def create(self, request, *args, **kwargs):
try:
return super().create(request, *args, **kwargs)
except IntegrityError as err:
if 'UNIQUE constraint' in err.message:
raise ValidationError({
'number': 'Number field should be unique.'
})
else:
raise IntegrityError(err)
如上所述,我调用super().create()
捕获异常,然后检查UNIQUE
消息是否存在,如果存在,则我再次引发验证错误,因此rest framework's exception handler
处理该错误。如果没有,我会再次引发错误。
此方法的问题是我正在检查消息UNIQUE
的唯一错误,该错误将来可能会更改。当然,我可以在保存之前将部门添加到serializer context
和validate
,但这可能会导致race condition
,那么best practice
可以用来处理这种情况吗?
答案 0 :(得分:0)
您可以在 StudentModelSerializer
类中使用UniqueTogetherValidator :)
示例
from rest_framework.validators import UniqueTogetherValidator
class StudentModelSerializer(serializers.ModelSerializer):
# ...
class Meta:
# ToDo items belong to a parent list, and have an ordering defined
# by the 'position' field. No two items in a given list may share
# the same position.
validators = [
UniqueTogetherValidator(
queryset=Student.objects.all(),
fields=('department', 'number')
)
]
覆盖 create()
的查看方法,
# views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from rest_framework import status
class StudentViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def create(self, request, *args, **kwargs):
request_data = request.data
request_data.update({"department": kwargs['pk']})
serializer = self.get_serializer(data=request_data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
# serializers.py
from rest_framework.validators import UniqueTogetherValidator
class StudentModelSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = ("number", "department")
validators = [
UniqueTogetherValidator(
queryset=Student.objects.all(),
fields=('department', 'number')
)
]
答案 1 :(得分:0)
更好的方法是将异常的pgcode与psycopg2错误代码进行比较:
from psycopg2 import errorcodes
class StudentViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def perform_create(self, serializer):
department = Department.objects.get(pk=self.kwargs['pk'])
serializer.save(department=department)
def create(self, request, *args, **kwargs):
try:
return super().create(request, *args, **kwargs)
except IntegrityError as err:
if err.__cause__.pgcode == errorcodes.UNIQUE_VIOLATION and \
"number" in err.args[0]
raise ValidationError({
'number': 'Number field should be unique.'
})
raise