我正在尝试使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
模型对象?
我也仅通过使用序列化器字段验证器(上面未显示)解决了这个问题,但是我也很想知道如何使用这种方法来解决此问题。也许这只是个坏主意,如果是的话,请告诉我原因。
答案 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
对象。