单个字段的DRF对象级别权限检查

时间:2018-08-29 15:25:21

标签: django django-rest-framework django-permissions

如何为模型添加权限,以便任何用户都可以添加新实例,但是只有登录用户才能添加特定属性?

Django 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

我看到了对自定义权限类的适当调用/打印,但是我仍然可以通过配方id的列表发出POST请求,并且不会出现错误消息。

注释-

  • 我收到两个带有相同信息/打印语句的POST请求,然后收到GET的第三个请求(添加后,它显示了新创建的实例-这是正确的行为,但是我不知道为什么要执行两个POST)

2 个答案:

答案 0 :(得分:2)

我认为一种更好的方法是使用2个不同的序列化器(其中一个具有Recipes作为可写字段,另一个没有),然后覆盖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从不检查对象级别权限。

答案 1 :(得分:2)

@changak提出的解决方案是一个很好的解决方案。将其包括在内是对提出的问题的更直接解决方案。在DRF中,Ionic明确地用于数据库中已经存在的对象,但是您可以使用has_object_permission。摘录自the docs,解释了为什么您看不到被称为has_permission的原因:

  

注意:实例级的has_object_permission方法仅适用于   如果视图级别的has_permission检查已通过,则调用此方法。

has_object_permission中,您仍然有权访问数据,并可以添加支票。假设您的has_permission有一个IngredientSerializer字段,则可以使用以下内容进行检查:

recipes