【问题标题】:How to assign Django object ownership without explicitly declaring an owner field on all models?如何在不明确声明所有模型的所有者字段的情况下分配 Django 对象所有权?
【发布时间】:2018-04-26 08:06:49
【问题描述】:

我目前正在尝试为我们的 Django 网站 API 确定每个用户对象的权限。

我有几个包含敏感信息的模型,我需要能够根据用户进行过滤。

对于其中一种模型的简化示例:

  • 餐厅,网站的主要客户。

  • 用户,当用户帐户为 创建的。因此,一家餐厅可以有很多用户,而且他们都是 应该只能访问该餐厅的信息。

  • 烤箱,属于特定餐厅。餐厅可以有 许多烤箱。

  • 食谱,属于烤箱。一个烤箱可以有很多不同的 食谱。

  • 配方结果,属于配方。可以有很多不同的 属于同一配方的配方结果(不同的成分 尝试过,等等)。

至少有 12 种以上的不同型号。特定餐厅的所有模型都必须对其他餐厅隐藏,毕竟我们不希望他们能够查看其他餐厅的食谱!

并非所有模型都有 user = models.ForeignKey(User)

无需进入我的每个模型并声明 owner = models.ForeignKey(User),有没有办法在我的 API 列表视图和详细视图中过滤它们?

目前我的 List API View 看起来像这样(简化示例):

class RecipeResultsListAPIView(ListAPIView):
    queryset = RecipeResults.objects.all()
    queryset = queryset.prefetch_related('oven')
    serializer_class = RecipeResultsListSerializer 
    filter_backends = (DjangoFilterBackend,)
    filter_fields = ('id', 'time', 'oven', 'recipe_name', 'recipe_description')
    pagination_class = ExpertPageNumberPagination

    def list(self, request):
       user = User.objects.get(username=request.user)
       restaurant = Restaurant.objects.get(user=user)
       ovens = Oven.objects.filter(restaurant=restaurant)
       queryset = RecipeResults.objects.filter(oven__in=ovens)
       serializer = RecipeResultsListSerializer(queryset, many=True, context={'request':request})
       return Response(serializer.data)

这个模型看起来像这样:

class RecipeResults(models.Model):
    time = models.DateTimeField()
    oven = models.ForeignKey(Oven, on_delete=models.CASCADE)
    recipe_name = models.CharField(max_length=20)
    recipe_description = models.CharField(max_length=50)

    def __str__(self):
        return str(self.time) + ': ' + self.recipe_name + ' = ' + self.recipe_description

    def __key(self):
        return self.oven, self.time, self.recipe_name

    def __eq__(self, y):
        return isinstance(y, self.__class__) and self.__key() == y.__key()

    def __hash__(self):
        return hash(self.__key())

    class Meta:
        unique_together=(('time','recipe_name', 'oven-'),)

具体看修改后的list方法,目前这个方法可以正常过滤API调用结果,只显示那些属于登录用户的Recipe Results。

我想弄清楚是否有更简单的方法可以做到这一点,因为对于每个模型,我都必须将所有权追溯到特定餐厅,这会很快变得混乱,因为我有 12 多个不同的模型。

我不确定在每个模型上声明“owner = models.ForeignKey(User)”是否可行。感觉在检索数据时会产生许多额外的步骤。

我也试过

class IsOwnerOrAdmin(BasePermission):
    """
    Custom permission to only allow owners of an object to see and edit it.
    Admin users however have access to all.
    """
    def has_object_permission(self, request, view, obj):
        # Permissions are only allowed to the owner of the snippet
        if request.user.is_staff:
            return True
        return obj.user == request.user

但这似乎没有正确过滤,此外,并非所有模型都分配有用户字段。

请记住,我是一名初级开发人员,我在工作中学到了很多东西。我只在公司的 API 方面工作。网站和架构已经在进行中,其他系统依赖于它,所以我尽量不要过多地修改架构或模型(如果可能的话,我想避免这种情况,但如果它是唯一的,我会这样做大大地)。起初,我也只是为了开发 API 而被带进来的。公司知道我是一名初级开发人员,我非常感谢在学习这个项目的同时有机会成长,但是这个问题似乎给我带来了比实际构建 API 的其余部分更多的麻烦网站。

如果我能在这方面得到任何帮助,我将不胜感激!

【问题讨论】:

标签: python django django-models django-rest-framework


【解决方案1】:

我认为在这种情况下您可能会从模型继承中受益。

您可以为受所有者影响的对象定义基本模型。

示例如下:

class OwnedModel(models.Model):
    owner = models.ForeignKey(User)

    class Meta:
        abstract = True

然后您可以简单地将其添加为其他模型的基础:

class SomeModel(OwnedModel):
    """
    This class already has the owner field
    """

这种方法的一个很大的缺点是您仍然需要一个会改变每个相关表的迁移。

如果不允许您这样做,您可以使用松散的、非关系的方法来执行此操作,例如使用django's permission model。您可以分配自动生成的权限字符串,例如:myapp.mymodel.pkey

最后一个替代方案是为您处理事情的第三方源应用程序:django-guardian

【讨论】:

  • 那么如果我使用继承自models.Model的自定义OwnedModel,然后将所有其他模型更改为继承自OwnedModel,它们也会继承自models.Model,因为OwnedModel是父类?
  • OwnedModel 应该声明为抽象的,否则每次查找都会涉及额外的 JOIN
  • @Mormoran 是的,它就是这样工作的。请务必将该模型声明为 Daniel 指出的抽象模型(编辑我的答案以包含它)。
猜你喜欢
  • 2019-07-22
  • 2020-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-07
  • 1970-01-01
  • 1970-01-01
  • 2016-11-13
相关资源
最近更新 更多