Обработка разрешения Django Rest Framework при создании связанных объектов

Метод has_object_permission разрешения на DRF, очевидно, не выполняется при Create, поскольку объект еще не существует. Однако существуют случаи, когда разрешение зависит от связанного объекта. Например:

class Daddy(models.Model):
    name = models.CharField(max_length=20)
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)


class Kiddy:
    title = models.CharField(max_length=12)
    daddy = models.ForeignKey(Daddy, on_delete=models.CASCADE)

Если мы хотим разрешить только владельцу Daddy создавать Kiddy этого Daddy, нам придется где-то подтвердить это.

Я знаю, что это действительно распространенное обсуждение, также упоминается в этом вопросе и во многих других. Это также обсуждается на самом DRF GitHub, где для этой цели было сделано обновление документации, и, ссылаясь на документацию DRF, она решает проблему здесь следующим предложением:

... Чтобы ограничить создание объекта, вам нужно реализовать проверку разрешения либо в классе Serializer, либо переопределить метод perform_create() вашего класса ViewSet.

Итак, ссылаясь на документацию DRF, мы можем сделать одно из следующих решений:

class KiddySerializer(viewsets.ModelViewSet):
    validate_daddy(self, daddy):
        if not daddy.owner == self.context['request'].user:
            raise ValidationError("You cannot create Kiddies of that Daddy")

        return daddy

или

class KiddyViewSet(ModelViewSet):
    def perform_create(self, serializer):
        if (self.request.user != serializer.validated_data['daddy'].owner)
            raise ValidationError("You cannot create Kiddies of that Daddy")
        
        serializer.save()

Теперь возникает проблема, которая также вызывает мой вопрос. Что если меня волнует информация, которая передается пользователю при несанкционированном запросе. Так, в случаях, когда Daddy не существует, пользователь получит: Invalid pk \"11\" - object does not exist, а в случаях, когда объект существует, но у пользователя нет доступа, он вернет You cannot create Kiddies of that Daddy

Я хочу показывать одно и то же сообщение в обоих случаях:

The Daddy does not exist or you don't have permission to use it.

Это возможно, если я использую PermissionClass, как показано ниже:

class OwnsDaddy(BasePermission):
    def has_permission(self, request, view):
        if not Daddy.objects.allowed_daddies(request.user).filter(pk=request.data['daddy']).exists():
            return False

это тоже будет работать, но поскольку разрешения проверяются перед сериализатором, если ID папочки, переданный пользователем, будет неверным (допустим, строка), возникнет ошибка 500. Мы можем предотвратить это с помощью условия try-except, но все равно это не совсем правильно.

Итак, в конце. Каким был бы хороший подход к этой проблеме?

Валидируйте в вашем сериализаторе вместо представления. Вы можете валидировать конкретное поле в validate_<field> или все поле в целом в validate функции

class YourModleSerializer(serializers.ModelSerializer):
    def validate_field(self, value):
        return value
        
    def validate(self, attrs):
        validated_data = super().validate(attrs)
        return validated_data
     

Создание пользовательского отношения полей было правильным подходом для моего случая.

from apps.models import Daddy
from rest_framework.serializers import PrimaryKeyRelatedField

class DaddyRelatedField(PrimaryKeyRelatedField):
    default_error_messages = {
        "required": _("This field is required."),
        "does_not_exist": _("The Daddy does not exist or you don't have permission to use it."),
        "incorrect_type": _("Incorrect type. Expected pk value, received {data_type}."),
    }

    def get_queryset(self):
        if self.read_only:
            return None
        queryset = Daddy.objects.all()
        return queryset.allowed_daddies(self.context["request"].user)
 

и затем на сериализаторе

class KiddySerializer(ModelSerializer):
    daddy = DaddyRelatedField()
Вернуться на верх