从权限类访问超链接的RelatedField对象

时间:2018-10-12 16:47:50

标签: python django django-rest-framework

我正在尝试使reddit之类的api后端。我想确保在特定subreddit中创建帖子(模型Post)的人都是该subreddit的成员(subreddit模型为Sub)。这是我最近的工作,虽然有效,但看起来很草率,还有一些上下文的序列化程序。

发布 permissions.py

class IsMemberOfSubOrReadOnly(BasePermission):
    def has_permission(self, request, view):

        if request.method in permissions.SAFE_METHODS:
            return True

        elif request.data:
            # prevent creation unless user is member of the sub
            post_sub_pk = get_pk_from_link(request.data['sub'])
            user = request.user
            user_sub_pks = [sub.pk for sub in user.subs.all()]
            if not (post_sub_pk in user_sub_pks):
                return False

    return True

发布 serializers.py

from .models import Post
from redditors.models import User
from subs.models import Sub

class PostSerializer(serializers.HyperlinkedModelSerializer):

    poster = serializers.HyperlinkedRelatedField(
        view_name='user-detail',
        #queryset=User.objects.all(),
        read_only=True
    )

    sub = serializers.HyperlinkedRelatedField(
        view_name='sub-detail',
        queryset=Sub.objects.all()
    )

    class Meta:
        model = Post
        fields = ('url', 'id', 'created', 'updated', 'title', 'body',
                    'upvotes', 'sub', 'poster')

此方法的问题在于,由于'sub'是Post序列化程序上的superlinkedRelatedField,我从request.data['sub']那里得到的只是字符串超链接url。然后,我有了一个函数get_pk_from_link,该函数使用正则表达式从网址末尾读取pk。然后,我可以用它来获取所需的实际模型并检查情况。如果有一种更直接的方法来访问请求中涉及的Sub模型,那就太好了。

我尝试搜索可用参数的字段,但找不到直接到达Sub对象的方法。是否可以通过超链接URL访问Sub模型对象?

我也仅通过使用序列化器字段验证器(上面未显示)解决了这个问题,但是我也很想知道如何使用这种方法来解决此问题。也许这只是个坏主意,如果是的话,请告诉我原因。

1 个答案:

答案 0 :(得分:1)

您是对的,解析URL并非可行之路。由于您想在创建Post对象之前执行权限检查,因此我怀疑您也不能使用object level permissions,因为DRF不会在get_object中调用CreateAPIView(因为对象在数据库中尚不存在。

考虑到这是“业务逻辑”检查,一种更简单的方法是根本不具有该权限类,并在视图的perform_create钩子中执行检查(我曾问过{{3} }关于此内容)

from rest_framework.exceptions import PermissionDenied

# assuming you have a view class like this one for creating Post objects
class PostList(generics.CreateApiView):
    # ... other view stuff

    def perform_create(self, serializer):
        sub = serializer.get('sub') # serializer is already validated so the sub object exists
        if not self.request.user.subs.filter(pk=sub.pk).exists():
            raise PermissionDenied(detail='Sorry, you are not a member of this sub.')
        serializer.save()

这省去了麻烦的麻烦,因为序列化程序应该直接为您提供Sub对象。