Проверка разрешений уровня объекта DRF для одного поля - PullRequest
0 голосов
/ 29 августа 2018

Как добавить разрешения для модели, чтобы любой пользователь мог добавить новый экземпляр, но только зарегистрированный пользователь может добавить определенный атрибут?

Джанго models.py:

class Ingredient(models.Model):
    name = models.CharField(max_length=100, unique=True)
    recipes = models.ManyToManyField(Recipe, related_name='ingredients', blank=True)

DRF views.py:

class IngredientViewSet(viewsets.ModelViewSet):
    queryset = Ingredient.objects.all()
    serializer_class = IngredientSerializer
    permission_classes = (IsUserForRecipeOrBasicAddReadOnly,)

DRF permissions.py:

class IsUserForRecipeOrBasicAddReadOnly(permissions.BasePermission):
    """
    Custom permission to only allow logged in users to add an ingredient AND associate its recipe(s).
    """
    message = 'You must be logged in to add an Ingredient to a Recipe.'

    # using this method so I can access the model obj itself
    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        if request.method in permissions.SAFE_METHODS:
            return True

        # Check if we are creating, and if the recipes are included, and if they are not a user.  If so, return False
        if request.method == 'POST' and obj.recipes.all().count() > 0 and request.user.is_anonymous:
            return False
        else:
            return True

Я вижу соответствующие вызовы / распечатки в пользовательский класс разрешений, но я все еще могу сделать запрос POST со списком рецептов id, и он не выдаст сообщение об ошибке.

Примечания -

  • Я получаю два запроса POST, оба с одинаковыми инструкциями information / print, а затем треть к GET (после добавления он показывает вновь созданный экземпляр - это правильное поведение, но я не знаю, почему проходят два поста)

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

Решение, предложенное @changak, является хорошим. Включая это как более прямое решение поставленного вопроса. В DRF has_object_permission явно для объекта , уже находящегося в базе данных , но вы можете использовать has_permission. Из документов этот отрывок объясняет, почему вы не видите, как has_object_permission вызывается:

Примечание: Метод has_object_permission уровня экземпляра будет только Вызывается, если проверки has_permission уровня представления уже пройдены.

В has_permission у вас все еще есть доступ к данным, и вы можете добавить чек. Предполагая, что у вашего IngredientSerializer есть поле recipes, вы можете проверить что-то вроде этого:

class IsUserForRecipeOrBasicAddReadOnly(permissions.BasePermission):
    """
    Custom permission to only allow logged in users to add an ingredient AND associate its recipe(s).
    """
    message = 'You must be logged in to add an Ingredient to a Recipe.'

    def has_permission(self, request, view):
        if view.action != 'create':
            # Handle everything but create at the object level.
            return True

        if not request.data.get('recipes'):
            return True

        return request.user and request.user.is_authenticated()
0 голосов
/ 29 августа 2018

Я думаю, что лучшим подходом было бы использовать 2 разных сериализатора (один из них имеет Рецепты как поле для записи, а другой - нет), затем переопределить get_serializer_class:

class yourMOdelviewset():
    ...
    ...
    def get_serializer_class(self):
        if self.action == 'create':
            if self.request.user.is_authenticated:
                return SerializerThatHasRecipesAsAWriteableField
            else:
                return SerializerThatHasNot
        return super().get_serializer_class()

p.s. Drf использует разрешение на уровне объекта для извлечения или обновления (в основном это должен быть уже объект), поскольку в create объекта еще нет, drf никогда не проверяет разрешение на уровне объекта.

...