如何使用 `django-filters` 编写将在整数字段上使用范围过滤器的 GraphQL 查询?

How to write a GraphQL query that will use a range filter on an integer field using `django-filters`?

我在我的 GraphQL API 中使用 graphene-pythondjango-filtersrelay。假设我有一个类型 FrameType,它有一个整数字段 time_offset,我希望能够在其上使用 range - 只询问帧time_offset 在给定范围内。我根据 graphene-python docs 和自定义 FilterSet:

准备了我的 schema.py
import django_filters
from graphene import ObjectType, relay
from graphene_django import DjangoObjectType, filter
from my_app.blabla import models



class FrameFilter(django_filters.FilterSet):
    time_offset = django_filters.RangeFilter()

    class Meta:
        model = models.Frame
        fields = ("time_offset",)


class FrameType(DjangoObjectType):
    class Meta:
        model = models.Frame
        filterset_class = FrameFilter
        interfaces = (relay.Node,)


class Query(ObjectType):
    frames = filter.DjangoFilterConnectionField(FrameType)

    class Meta:
        abstract = True

但是,我现在不知道如何查询timeOffset字段。我在网上找不到 django_filters.RangeFilter 字段的示例。这是我试过的查询:

query Frame {
  frames(first: 20, timeOffset: "{\"gt\":\"4350\", \"lt\":\"5000\"}") {
    edges {
      node {
        timeOffset
    }
  }
}

...还有这些选择:

timeOffset: "{\"gt\":4350, \"lt\":5000}"
timeOffset: "{\"start\":\"4350\", \"end\":\"5000\"}"
timeOffset: "{\"min\":\"4350\", \"max\": \"4500\"}"
timeOffset: "[\"4350\", \"5000\"]"
timeOffset: "[4350, 5000]"
timeOffset: "[4350]"
timeOffset: "4350,5000"

这些查询不会引发任何错误,但它们也不会过滤(返回所有结果)。我迷路了,我不确定我是否还没有找到正确的语法,或者我的后端代码可能有一些错误。我应该如何使用和查询字段上的 django_filters.RangeFilter

遗憾的是,这是不可能的。但是,有一个解决方法

将您的过滤器class调整为

<b>def custom_range_filter_method(queryset, field_name, value):
    if value:
        queryset = queryset.filter(**{f'{field_name}__range': value.split(',')})
    return queryset</b>


class FrameFilter(django_filters.FilterSet):
    time_offset = <b>filters.Filter(method=custom_range_filter_method)</b>

    class Meta:
        model = models.Frame
        fields = ("time_offset",)

现在用

查询架构
query Frame {
  frames(first: 20, <b>timeOffset: "4350,5000"</b>) {
    edges {
      node {
        timeOffset
    }
  }
}

参考

您可以在 Django 的查询集级别处理 range 选项,而不会干扰现有的中继查询。

在你的情况下,

  1. start_time_offsetend_time_offset 参数传递给 DjangoConnectionField
  2. 覆盖resolve_frames
  3. 如果 start_time_offsetend_time_offset 由用户提供 return objects.all()
  4. ,则在 django 查询集上进行过滤
class Query(ObjectType):
    frames = filter.DjangoFilterConnectionField(FrameType, start_time_offset=graphene.Int(), end_time_offset=graphene.Int())

    def resolve_frames(self, info, start_time_offset=None, end_time_offset=None, **kwargs):
        if start_time_offset and end_time_offset:
            return Frame.objects.filter(time_offset__range=(start_time_offset, end_time_offset))
        elif start_time_offset:
            return Frame.objects.filter(time_offset__gte=start_time_offset)
        elif end_time_offset:
            return Frame.objects.filter(time_offset__lte=end_time_offset)
        return Frame.objects.all()

现在您可以使用中继提供的常规过滤器对其进行查询:

query Frame {
  frames(last: 5, startTimeOffset: 4350, endTimeOffset:5000) {
    edges {
      node {
        timeOffset
    }
  }
}

它有点旧,但由于它可能对其他人有帮助,您可以查看此 thread, it used django filterset for DateRangeFiled and I think you can use a similar approach for integers using filterset RangeFilter. Also check this 以获取有关石墨烯中过滤器集的更多信息。

您需要做的就是更新您的 FrameType class,如下所示:

class FrameType(DjangoObjectType):
    class Meta:
        model = models.Frame
        filterset_fields = {
            'time_offset': ['range']
        }
        interfaces = (relay.Node,)

您不需要为此自定义过滤器集。 然后可以查询如下:

query Frame {
    frames(first: 20, timeOffset_Range: ["4350", "5000"]) {
    edges {
      node {
        timeOffset
    }
  }
}