基于django-guardian权限修改ModelViewSet的queryset进行many2many关系查询
Modifying ModelViewSet's queryset for many2many relation query based on django-guardian permissions
我得到了 2 个模型,它们以 ManyToMany 方式连接,如下所示。 我想要实现的目标是;仅当用户对该人所属的列表具有至少查看权限时,才为用户提供服务。
这是我的简化模型;
class List(TimeStampedModel, UserAwareModel):
"""Model definition for List."""
name = models.CharField(_('list name'), max_length=50)
...
class Person(TimeStampedModel, UserAwareModel):
"""Model definition for Person."""
member = models.ManyToManyField(List, blank=True)
...
这是我的看法;
class ListViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows Lists to be viewed or edited.
"""
queryset = List.objects.all()
serializer_class = ListSerializer
permission_classes = (CustomObjectPermissions, DjangoModelPermissions)
filter_backends = (filters.DjangoObjectPermissionsFilter,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class PersonViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows People to be viewed or edited.
"""
# queryset = Person.objects.all().order_by('-created')
serializer_class = PersonSerializer
permission_classes = (DjangoModelPermissions, )
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def get_queryset(self):
user = self.request.user
allowed_lists = get_objects_for_user(user, ('view_list'), klass=List)
queryset = set()
for lst in allowed_lists:
queryset.add(Person.objects.filter(member=lst))
return queryset
更新:感谢 mehamasum。用最近的更新回溯(似乎与我的 get_queryset 覆盖更相关)
更新 2: 再次感谢 mehamasum。现在我的代码可以正常工作,但是我的测试用户在 'List 1' 上只有查看权限,仍然可以在 people 端点获得所有用户(甚至属于其他列表)。 --代码有什么问题?--
更新 3: 出现 get_obejcts_for_user 函数没有 return 结果正确,正如 mehamasum 在评论。还在寻找原因。
看起来你的错误被抛出是因为你没有在 /app/really_simple_contact_management/urls.py
:
中为你的路线指定 basename
router.register(r'people', PersonViewSet)
来自docs:
basename - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the queryset attribute of the viewset, if it has one. Note that if the viewset does not include a queryset attribute then you must set basename when registering the viewset.
将上面的行替换为:
router.register(r'people', PersonViewSet, base_name='people',)
根据您的喜好提供一个有意义的字符串 base_name
,错误就会消失。
更新:
您正在手动构建并返回 set
, but the get_queryset
function expected an actual Django QuerySet。
您可以使用 __in
operator:
重写您的函数
def get_queryset(self):
user = self.request.user
allowed_lists = get_objects_for_user(user, ('view_list'), klass=List)
return Person.objects.filter(member__in=allowed_lists)
我得到了 2 个模型,它们以 ManyToMany 方式连接,如下所示。 我想要实现的目标是;仅当用户对该人所属的列表具有至少查看权限时,才为用户提供服务。
这是我的简化模型;
class List(TimeStampedModel, UserAwareModel):
"""Model definition for List."""
name = models.CharField(_('list name'), max_length=50)
...
class Person(TimeStampedModel, UserAwareModel):
"""Model definition for Person."""
member = models.ManyToManyField(List, blank=True)
...
这是我的看法;
class ListViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows Lists to be viewed or edited.
"""
queryset = List.objects.all()
serializer_class = ListSerializer
permission_classes = (CustomObjectPermissions, DjangoModelPermissions)
filter_backends = (filters.DjangoObjectPermissionsFilter,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class PersonViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows People to be viewed or edited.
"""
# queryset = Person.objects.all().order_by('-created')
serializer_class = PersonSerializer
permission_classes = (DjangoModelPermissions, )
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def get_queryset(self):
user = self.request.user
allowed_lists = get_objects_for_user(user, ('view_list'), klass=List)
queryset = set()
for lst in allowed_lists:
queryset.add(Person.objects.filter(member=lst))
return queryset
更新:感谢 mehamasum。用最近的更新回溯(似乎与我的 get_queryset 覆盖更相关)
更新 2: 再次感谢 mehamasum。现在我的代码可以正常工作,但是我的测试用户在 'List 1' 上只有查看权限,仍然可以在 people 端点获得所有用户(甚至属于其他列表)。 --代码有什么问题?--
更新 3: 出现 get_obejcts_for_user 函数没有 return 结果正确,正如 mehamasum 在评论。还在寻找原因。
看起来你的错误被抛出是因为你没有在 /app/really_simple_contact_management/urls.py
:
basename
router.register(r'people', PersonViewSet)
来自docs:
basename - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the queryset attribute of the viewset, if it has one. Note that if the viewset does not include a queryset attribute then you must set basename when registering the viewset.
将上面的行替换为:
router.register(r'people', PersonViewSet, base_name='people',)
根据您的喜好提供一个有意义的字符串 base_name
,错误就会消失。
更新:
您正在手动构建并返回 set
, but the get_queryset
function expected an actual Django QuerySet。
您可以使用 __in
operator:
def get_queryset(self):
user = self.request.user
allowed_lists = get_objects_for_user(user, ('view_list'), klass=List)
return Person.objects.filter(member__in=allowed_lists)