【问题标题】:Django Rest Framework Filtering an object in get_queryset methodDjango Rest Framework在get_queryset方法中过滤对象
【发布时间】:2021-07-16 02:34:55
【问题描述】:

基本上,我有一个目录视图集。在列表视图中,我想进行一些过滤并相应地返回。 相关目录模型字段是:

class Catalog(models.Model):
    name = models.CharField(max_length=191, null=True, blank=False)
    ...
    team = models.ForeignKey(Team, on_delete=models.CASCADE, editable=False, related_name='catalogs')
    whitelist_users = models.JSONField(null=True, blank=True, default=list) # If white list is null, it is open to whole team

 

Views.py

class CatalogViewSet(viewsets.ModelViewSet):
    permission_classes = (IsOwnerAdminOrRestricted,)

    def get_queryset(self):
        result = []
        user = self.request.user
        catalogs = Catalog.objects.filter(team__in=self.request.user.team_set.all())
        for catalog in catalogs:
            if catalog.whitelist_users == [] or catalog.whitelist_users == None:
                # catalog is open to whole team
                result.append(catalog)
            else:
                # catalog is private
                if user in catalog.whitelist_users:
                    result.append(catalog)
        return result

这就是我的逻辑;

1 - 如果目录的团队是当前用户的团队之一,则获取目录对象。

2 - 检查 catalog.whitelist_users 是否包含当前用户。 (还有一个例外,如果没有,则意味着它对整个团队开放,所以我可以在列表视图中显示它。)

现在这有效,但由于我返回一个数组,它没有正确找到详细对象。我的意思是 /catalog/ID 不能正常工作。

我是 DRF 的新手,所以我猜这里有问题。您将如何更好地实施此过滤?

【问题讨论】:

    标签: django django-rest-framework django-rest-viewsets


    【解决方案1】:

    想到的一个简单解决方案就是创建一个 PK 列表并再次过滤,这样您就可以返回一个查询集。不是最有效的解决方案,但应该可以:

    def get_queryset(self):
        pks = []
        user = self.request.user
        catalogs = Catalog.objects.filter(team__in=user.team_set.all())
        for catalog in catalogs:
            if catalog.whitelist_users == [] or catalog.whitelist_users == None:
                # catalog is open to whole team
                pks.append(catalog.pk)
            else:
                # catalog is private
                if user in catalog.whitelist_users:
                    pks.append(catalog.pk)
        return Catalog.objects.filter(id__in=pks)
    

    【讨论】:

      【解决方案2】:

      正如方法名称所暗示的,您需要返回一个查询集。此外,如果没有必要,请避免遍历查询集。最好在单个数据库命中中执行此操作。对于复杂的查询,您可以使用Q object

      from django.db.models import Q
      
      # ...
      
          def get_queryset(self):
              user = self.request.user
              catalogs = Catalog.objects.filter(
                         Q(whitelist_users__in=[None, []]) | Q(whitelist_users__contains=user),
                         team__in=user.team_set.all())
                                                              
              return catalogs
      

      现在我不能 100% 确定 whitelist_users__contains=user 会起作用,因为它取决于您如何构建 JSON,但想法就在那里,您只需要调整它包含的内容。

      这将比在 python 中循环更有效,并且会尊重 get_queryset 的含义。

      【讨论】: