Django限制对用户对象的访问

时间:2019-01-15 05:58:22

标签: django user-permissions

我有同时属于组织的Node和User模型。我想确保用户只能看到属于其组织的Node实例。

为此,我想用返回一个由用户拥有的过滤结果的query_set的对象覆盖Node Objects Manager。

基于https://docs.djangoproject.com/en/2.1/topics/db/managers/#modifying-a-manager-s-initial-queryset 我有相关的 models.py 代码如下:

class Organisation(models.Model):
    users = models.ManyToManyField(User, related_name='organisation')
    ...

class UserNodeManager(models.Manager):
    def get_queryset(self, request):
        return super().get_queryset().filter(organisation=self.request.user.organisation.first())


class Node(models.Model):
    organisation = models.ForeignKey(
        Organisation, related_name='nodes', on_delete=models.CASCADE)

    uuid = models.UUIDField(primary_key=True, verbose_name="UUID")
    ...

    objects = UserNodeManager

views.py

class NodeListView(LoginRequiredMixin, generic.ListView):
    model = Node

编辑 我可以将自定义query_set添加到各个视图,并且确实可以按以下方式工作:

views.py

class NodeListView(LoginRequiredMixin, generic.ListView):
    model = Node

    def get_queryset(self):
        return Node.objects.filter(organisation__users__id=self.request.user.pk)

但是,我的意图是进行DRY处理并在单个点上覆盖“主” query_set方法,以便任何视图(例如,表单下拉列表,API端点)都将执行用户受限的查询,而无需其他代码。

例如,我正在使用django的通用列表视图,该表单具有用于添加Scan对象的表单,该表单要求用户选择Scan所属的Node。该表单当前显示其他组织的节点,这与我需要的权限逻辑背道而驰。

不幸的是,被覆盖的Node.objects属性似乎没有任何效果,任何用户都可以看到所有Node。我采用正确的方法了吗?

3 个答案:

答案 0 :(得分:1)

我认为问题出在这里

objects = UserNodeManager

您需要像这样启动UserNodeManager实例:

objects = UserNodeManager()

此外,在调用YourModel.objects.all()方法(在视图中从get_queryset方法调用)时,它应该引发错误,因为在调用get_queryset()方法时,它不会传递{{ 1}}。因此,我认为这是一种更好的方法:

request

或者您可以创建一个新的管理器方法(可选):

class UserNodeManager(models.Manager):
    def all(self, request=None):
       qs = super(UserNodeManager, self).all()
       if request:
          return qs.filter(...)
       return qs

也在视图中更新:

class UserNodeManager(models.Manager):
    def user_specific_nodes(self, request):
       return self.get_queryset().filter(...)

更新

来自评论

问题是,您需要将class NodeListView(LoginRequiredMixin, generic.ListView): model = Node def get_queryset(self): return Node.objects.all(self.request) # where you can obviously use filter(...) or Model.objects.user_specific_nodes(self.request) request传递给filter()。在通用视图中,all()方法不会将该信息传递给get_queryset。因此,您需要通过任何一种方式。还有另一种使用django-crequest这样的中间件的方法。您可以像这样使用它:

all()

答案 1 :(得分:0)

实现此目标的最佳方法是使用组和自定义权限。您可以为每个组织添加一个组,并为您的节点上的这些组设置正确的权限。

看看这篇文章,可能会有所帮助:User Groups with Custom Permissions in Django

答案 2 :(得分:0)

@ruddra再次感谢您的指导。

尽管您的中间件示例对我没有影响(因为用户仍然可以看到其他对象),但我能够将其与django文档一起使用,最终实现类似于以下内容的Manager:

class UserDeviceManager(models.Manager):
    def get_queryset(self):
        request = CrequestMiddleware.get_request()
        return super().get_queryset().filter(organisation=request.user.organisation)