Лучший способ обработать ошибку unique together — Django 2.2?

#python #django #django-models #django-rest-framework

#питон #джанго #django-модели #django-rest-фреймворк #python #django #django-rest-framework

Вопрос:

Я знаю, что этот вопрос задавался много раз, но я все еще не могу найти правильное решение., допустим, у меня есть модель, подобная follow

 class Student(models.Model):
    number = models.IntegerField()
    department = models.ForeignKey(Department, on_delete=models.CASCADE)
    class Meta:
       constraints = [
                     models.UniqueConstraint(fields=['department', 'number'])
                     ]
  

и мой сериализатор выглядит как follow.

 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. можете ли вы показать конфигурацию URL?

2. Конечный URL-адрес должен быть таким [POST] http://localhost/departments/{pk}/students

3. проверьте мой ответ ниже 🙂

Ответ №1:

Лучшим способом может быть сравнение 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