Django限制对用户对象的访问
我有节点和用户模型,它们都属于一个组织。我希望确保用户只能看到属于其组织的节点实例 为此,我想用一个返回用户拥有的过滤结果的查询集的管理器覆盖节点对象管理器 基于 我拥有的相关models.py代码如下:Django限制对用户对象的访问,django,user-permissions,Django,User Permissions,我有节点和用户模型,它们都属于一个组织。我希望确保用户只能看到属于其组织的节点实例 为此,我想用一个返回用户拥有的过滤结果的查询集的管理器覆盖节点对象管理器 基于 我拥有的相关models.py代码如下: class Organisation(models.Model): users = models.ManyToManyField(User, related_name='organisation') ... class UserNodeManager(models.Manag
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
编辑
我可以将自定义查询集添加到各个视图,具体操作如下:
views.py
但是,我的目的是保持干爽,在单个点重写“主”查询集方法,以便任何视图(例如表单下拉列表、API端点)都可以执行用户限制查询,而无需额外代码
例如,我正在使用django的通用列表视图,它有一个用于添加扫描对象的表单,该表单要求用户选择扫描所属的节点。该表单当前显示来自其他组织的节点,这与我需要的权限逻辑相反
不幸的是,重写的Node.objects属性似乎没有任何效果,任何用户都可以看到所有节点。我是否采取了正确的方法?实现这一点的最佳方法是使用组和自定义权限。您可以为每个组织添加一个组,并在节点上为这些组设置正确的权限
看看这篇文章,可能会有所帮助:我认为问题出在这里:
objects = UserNodeManager
您需要像这样启动UserNodeManager
实例:
objects = UserNodeManager()
from crequest.middleware import CrequestMiddleware
class UserNodeManager(models.Manager):
def all(self):
qs = super(UserNodeManager, self).all()
request = CrequestMiddleware.get_request()
return qs.filter(...)
另外,当您调用YourModel.objects.all()
方法(从视图中的get\u queryset
方法调用)时,它应该抛出错误,因为当它调用get\u queryset()
方法时,它不会传递请求。因此,我认为这是一个更好的方法:
class UserNodeManager(models.Manager):
def all(self, request=None):
qs = super(UserNodeManager, self).all()
if request:
return qs.filter(...)
return qs
或者您可以创建一个新的管理器方法,如下所示(可选):
还可以在视图中更新:
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\u queryset
方法不会将该信息传递给all()
。所以你必须通过这两条路。还有另一种方法,使用这样的中间件。您可以这样使用它:
objects = UserNodeManager()
from crequest.middleware import CrequestMiddleware
class UserNodeManager(models.Manager):
def all(self):
qs = super(UserNodeManager, self).all()
request = CrequestMiddleware.get_request()
return qs.filter(...)
@鲁德拉再次感谢你的指导
虽然您的中间件示例对我没有影响(因为用户仍然可以看到其他人的对象),但我能够将其与django文档结合使用,最终实现类似于以下内容的管理器:
class UserDeviceManager(models.Manager):
def get_queryset(self):
request = CrequestMiddleware.get_request()
return super().get_queryset().filter(organisation=request.user.organisation)
您是否尝试此return super().get_queryset().filter(organization\uu users\u id=self.request.user.pk)
这会导致错误:相关字段的查找无效:users\u id尝试更改为具有2个下划线,如organization\uu users\uu id
@SergeyPugach:yes,它适用于视图中定义的查询集,但作为上面UserNodeManager类的一部分仍然没有效果。如何调用get\u queryset
方法?您是否覆盖了所有的管理器方法(即更新、获取、删除)?感谢Raydel的建议,但如果我的思路正确,并且可以在模型级别定义查询集,那么与定义其他组相比,这似乎需要更少的工作/维护。其目的是防止不安全的直接对象引用。@user1330我理解,谢谢你,ruddra,我可以为每个视图添加自定义查询集,但我正在寻找一个可以更改的点,该点将限制应用程序范围内的节点查询。我更新了问题以进行澄清。在您的回答中,您提供了添加UserNodeManager
的选项,并声明将覆盖NodeListView
的get\u queryset
。但是,我理解这一点,因为所有视图都需要重复相同的get_queryset
,这是不干燥的。我的意图是更改signleUserNodeManager
,以便任何查询(来自任何HTML视图、API端点等)都使用此“主”查询集。这可能吗?@user1330734请查看答案的更新部分。我希望有帮助。
class UserDeviceManager(models.Manager):
def get_queryset(self):
request = CrequestMiddleware.get_request()
return super().get_queryset().filter(organisation=request.user.organisation)