Настройте Django Rest Framework на заполнение поля user пользователем, вошедшим в систему, перед проверкой сериализатора

У меня есть конечная точка API, поддерживающая метод POST, который работает, если я включаю user в полезную нагрузку запроса.

Я хотел бы исключить это из полезной нагрузки запроса и заставить DRF просто использовать зарегистрированного пользователя.

Я получаю следующую ошибку, когда опускаю user из тела запроса:

HTTP 400 Bad Request
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "user": [
        "This field is required."
    ]
}

Похоже, что это происходит, когда фреймворк вызывает serializer.is_valid().

Как настроить DRF на заполнение пользователя из request.user, чтобы проверка сериализатора не завершилась неудачей?

models.py

class Task(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    domain = models.CharField(max_length=settings.MAX_CHAR_COUNT)

сериализаторы

class TaskSerializer(serializers.HyperlinkedModelSerializer):
    user = serializers.SlugRelatedField(queryset=CustomUser.objects.all(), slug_field='email')

    class Meta:
        model = Task
        fields = ['id', 'created', 'domain', 'user']

views.py

class TaskApiViewSet(viewsets.ModelViewSet):
    permission_classes = [permissions.IsAuthenticated,]
    serializer_class = TaskSerializer
    task_service = TaskService()

    """
        GET /api/tasks
    """
    def get_queryset(self):
        user = self.request.user

        if user.is_superuser:
            return Task.objects.all()

        return Task.objects.filter(user=self.request.user)

    """
        POST /api/tasks
    """
    def create(self, request):
        domain = request.data['domain']

        can_create_task = self.task_service.can_create_task(user=request.user, domain_name=domain)

        if not can_create_task:
            raise PermissionDenied(code=None, detail=None)

        return super().create(request)

Возможно, вам придется полностью переопределить метод ViewSet вашего create(), чтобы иметь возможность передать измененный dict в сериализатор. Вы можете просто скопировать исходное определение

После этого вы можете добавить пользователя в данные запроса, сделав копию, изменив ее и передав сериализатору:

def create(self, request, *args, **kwargs):
    user_id = self.request.user.id
    domain = request.data['domain']
    new_data = request.data.copy()
    new_data.update({"user": user_id})
        
    can_create_task = self.task_service.can_create_task(user=request.user, domain_name=domain)

    if not can_create_task:
            raise PermissionDenied(code=None, detail=None)

    # original definition of create() with new_data
    serializer = self.get_serializer(data=new_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)

Правильный способ сделать это, согласно Django Rest Framework, заключается в переопределении метода perform_create() в вашем ViewSet, который отвечает за создание экземпляра модели с помощью сериализатора. Вы можете передать сериализатору дополнительные данные:

def perform_create(self, serializer):
    serializer.save(user=self.request.user)

Оригинальное определение метода perform_create() можно найти здесь.

Простым решением вашей проблемы будет использование поля "extra_kwargs" внутри вашего класса Meta, для вашего случая это будет что-то вроде этого:

class Meta:
        model = Task
        fields = ['id', 'created', 'domain']
        extra_kwargs = {'user': {'default': serializers.CurrentUserDefault()}}
Вернуться на верх