Лучший способ обработать уникальную ошибку вместе - Django 2.2? - PullRequest
1 голос
/ 05 апреля 2019

Я знаю, что этот вопрос задавался много раз, но я все еще не могу найти правильное решение. Допустим, у меня есть модель, подобная 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, теперь отдел выбирается из pk, переданного в URL. То, как я обрабатываю уникальную ошибку, похоже на следующее.

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 для обработки подобного сценария?

1 Ответ

0 голосов
/ 05 апреля 2019

Вы можете использовать UniqueTogetherValidator in StudentModelSerializer class:)

Пример

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')
            )
        ]

UPDATE-1

переопределить create() метод просмотра,

# views.py
from rest_framework.viewsets import ModelViewSet
<b>from rest_framework.response import Response
from rest_framework import status</b>


class StudentViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def create(self, request, *args, **kwargs):
        <b>request_data = request.data
        request_data.update({"department": kwargs['pk']})</b>
        serializer = self.get_serializer(<b>data=request_data</b>)
        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
<b>from rest_framework.validators import UniqueTogetherValidator</b>


class StudentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = ("number", <b>"department"</b>)
        <b>validators = [
            UniqueTogetherValidator(
                queryset=Student.objects.all(),
                fields=('department', 'number')
            )
        ]</b>
...