【问题标题】:Multiple filter parameters in API endpoint - DRFAPI 端点中的多个过滤器参数 - DRF
【发布时间】:2019-08-01 02:04:00
【问题描述】:

我将Django rest framework 用于我的 REST API。我有一些用于过滤的参数的端点,如下所示:

# in my view
from_date = self.request.query_params.get('from', None)
to_date = self.request.query_params.get('to', None)
category = self.request.query_params.get('category', None)
color = self.request.query_params.get('color', None)

然后我在选择数据的地方有 if/else 代码。第一次只有两个参数所以没问题,但后来我添加了更多参数进行过滤,现在我认为这段代码看起来不太好,我认为需要进行一些优化。此外,不同参数的组合现在不起作用。

实际代码(在我看来):

if from_date is not None and to_date is not None:
    something = Something.objects.filter(send_date__range=(from_date, to_date))
elif category:
    something = Something.objects.filter(values__something__category__name__iexact=category)
elif color:
    something = Something.objects.filter(values__something__color__name__iexact=color)
else:
    something = Something.objects.all()

是否有任何选项,内置于 DRF 或 Django 中的某种类型的组合过滤,考虑到值可以为无,我如何将参数组合成一个大查询?

例如,如果用户使用日期发送 from_dateto_date 查询过滤数据。但用户也希望使用 from_dateto_datecolor 或其他参数组合过滤数据。有些参数可以省略。该代码只是最小版本。我有更多参数,我不想为每个参数组合编写 if 和 else。

类似的东西(但如果其中一个值为 None 则没有此值查询将起作用)

Something.objects.filter(send_date__range=(from_date, to_date))
    .filter(values__something__category__name__iexact=category)
    .filter(values__something__color__name__iexact=color)

【问题讨论】:

  • 例如,如果 from_date 和 to_date 为 None,那么您要为查询提供什么默认值?或者您到底希望查询做什么?
  • 我会扩展我的问题。给我一分钟
  • 好的,添加了一些信息。我不想为每个参数组合编写 if/else,因为用户可以使用日期和颜色或仅使用颜色、颜色和类别等过滤数据
  • just gona 推荐 django 过滤器 (django-filter.readthedocs.io/en/master),它是 django 中过滤的一个疯狂的综合解决方案,并且可以与 DRF 轻松集成
  • 我也有同样的想法,使用 django-filters,但如果有其他更简单的解决方案,我就陷入了沉思。

标签: django django-rest-framework django-filter


【解决方案1】:

将参数转换为查询过滤器不能完全自动化,因为只有您知道哪些参数应该以哪种方式过滤哪些字段。但是,除非你有 orand 标准的疯狂组合,否则它应该非常简单,因为你可以链接 .filter() 调用而没有任何限制,只要你想 and 所有标准:

something = Something.objects.all()
if from_date and to_date:
    something = something.filter(send_date__range=(from_date, to_date))
if category:
    something = something.filter(values__something__category__name__iexact=category)
if color:
    something = something.filter(values__something__color__name__iexact=color)

这涵盖了所有组合,看起来很容易管理,不是吗?

如果您需要or(某些)您的条件,则需要Q objects,但是您可以以相同的方式链接:

from django.db.models.query_utils import Q

qq = Q()
if category:
    qq = qq | Q(values__something__category__name__iexact=category)
if color:
    qq = qq | Q(values__something__color__name__iexact=color)
something = Something.objects.filter(qq)

【讨论】:

  • 嗯,很简单。我可以想到以这种方式组合过滤结果:-)谢谢
  • 我试过这个,但如果我使用日期然后类别。来自日期的查询集在类别中重复(例如,使用日期的 3 个结果,在应用类别后应该只有 2 条记录,但有 2*4 - 每条记录在查询集中是 4 倍)。这很奇怪。 Django的版本可能有问题吗?好的。这可能是因为表格的加入。 Distinct应该可以解决这个问题
  • 我不认为重复来自链接过滤器;您没有看到仅与类别查询相同的重复项吗?它确实与加入有关,并且可能是可以避免的(取决于模型和您实际需要的数据)。但如果 distinct() 解决了您的问题,那就太好了。
  • 尝试将order_by() 附加到最终查询,这会取消相关表中存在的任何默认排序。如果您的重复项消失了,那么您就找到了罪魁祸首。如果他们不这样做,看看发生这种情况的模型和查询会很有趣。
【解决方案2】:

这是我用于自定义过滤的模式。如果您最终需要围绕每个过滤器参数进行更多自定义实现,您可以使用策略设计模式之类的东西,它可以很好地摆脱 if-else 链。

FILTER_PARAMS = {'from': 'from_date', 'to': 'to_date', 'category': 'values__something__category__name__iexact', 'color': 'values__something__color__name__iexact'}

def get(self):
        filter_params = self.get_filter_params(self.request.query_params)
        qs = queryset.filter(**filter_params)

def get_filter_params(self, query_params):
        fields = {}

        for k, v in query_params.items():
                if k in self.FILTER_PARAMS:
                    fields[self.FILTER_PARAMS[k]] = v
        return fields

【讨论】:

  • 谢谢@Braden Hold 我会检查结构并尝试一下
  • 没问题。还用字典替换了元组,因此您的 query_params 不必匹配 django 过滤器约定。
猜你喜欢
  • 2020-01-21
  • 2019-04-16
  • 2020-08-22
  • 2020-11-03
  • 1970-01-01
  • 2021-10-20
  • 1970-01-01
  • 2021-03-22
  • 2021-08-11
相关资源
最近更新 更多