Django 限制对用户对象的访问
Django restrict access to user objects
我有节点和用户模型,它们都属于一个组织。我想确保用户只会看到属于他们组织的节点实例。
为此,我想用 returns 用户拥有的过滤结果 query_set 覆盖节点对象管理器。
基于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 并在单个点覆盖 'master' query_set 方法,以便任何视图(例如表单下拉列表、API 端点)都将执行没有附加代码的用户限制查询。
例如,我正在使用 django 的通用列表视图,它有一个用于添加 Scan 对象的表单,该表单要求用户 select Scan 所属的节点。该表单当前显示来自其他组织的节点,这违反了我需要的权限逻辑。
不幸的是,覆盖的 Node.objects 属性 似乎没有任何效果,任何用户都可以看到所有节点。我采取的方法正确吗?
实现此目的的最佳方法是使用组和自定义权限。您可以为每个组织添加一个组,并为这些组在您的节点上设置正确的权限。
看看这篇文章,它可能会有所帮助:User Groups with Custom Permissions in Django
我认为问题出在这里:
objects = UserNodeManager
您需要像这样启动 UserNodeManager
实例:
objects = UserNodeManager()
另外,调用YourModel.objects.all()
方法(在视图中是从get_queryset
方法调用的)应该会抛出错误,因为调用get_queryset()
方法时,它并没有通过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)
更新
来自评论
问题是,您需要用 filter()
或 all()
传递 request
。在通用视图中,get_queryset
方法不会将该信息传递给 all()
。所以你需要通过任何一种方式。还有另一种方法,使用这样的中间件 django-crequest。你可以这样使用它:
from crequest.middleware import CrequestMiddleware
class UserNodeManager(models.Manager):
def all(self):
qs = super(UserNodeManager, self).all()
request = CrequestMiddleware.get_request()
return qs.filter(...)
@ruddra 再次感谢您的指导。
虽然你的中间件示例对我没有影响(因为用户仍然可以看到其他人的对象),但我能够将它与 django 文档一起使用,最终实现管理器类似于:
class UserDeviceManager(models.Manager):
def get_queryset(self):
request = CrequestMiddleware.get_request()
return super().get_queryset().filter(organisation=request.user.organisation)
我有节点和用户模型,它们都属于一个组织。我想确保用户只会看到属于他们组织的节点实例。
为此,我想用 returns 用户拥有的过滤结果 query_set 覆盖节点对象管理器。
基于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 并在单个点覆盖 'master' query_set 方法,以便任何视图(例如表单下拉列表、API 端点)都将执行没有附加代码的用户限制查询。
例如,我正在使用 django 的通用列表视图,它有一个用于添加 Scan 对象的表单,该表单要求用户 select Scan 所属的节点。该表单当前显示来自其他组织的节点,这违反了我需要的权限逻辑。
不幸的是,覆盖的 Node.objects 属性 似乎没有任何效果,任何用户都可以看到所有节点。我采取的方法正确吗?
实现此目的的最佳方法是使用组和自定义权限。您可以为每个组织添加一个组,并为这些组在您的节点上设置正确的权限。
看看这篇文章,它可能会有所帮助:User Groups with Custom Permissions in Django
我认为问题出在这里:
objects = UserNodeManager
您需要像这样启动 UserNodeManager
实例:
objects = UserNodeManager()
另外,调用YourModel.objects.all()
方法(在视图中是从get_queryset
方法调用的)应该会抛出错误,因为调用get_queryset()
方法时,它并没有通过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)
更新
来自评论
问题是,您需要用 filter()
或 all()
传递 request
。在通用视图中,get_queryset
方法不会将该信息传递给 all()
。所以你需要通过任何一种方式。还有另一种方法,使用这样的中间件 django-crequest。你可以这样使用它:
from crequest.middleware import CrequestMiddleware
class UserNodeManager(models.Manager):
def all(self):
qs = super(UserNodeManager, self).all()
request = CrequestMiddleware.get_request()
return qs.filter(...)
@ruddra 再次感谢您的指导。
虽然你的中间件示例对我没有影响(因为用户仍然可以看到其他人的对象),但我能够将它与 django 文档一起使用,最终实现管理器类似于:
class UserDeviceManager(models.Manager):
def get_queryset(self):
request = CrequestMiddleware.get_request()
return super().get_queryset().filter(organisation=request.user.organisation)