简体   繁体   English

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

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

I am using graphene-python , django-filters and relay in my GraphQL API.我在我的 GraphQL API 中使用了graphene-pythondjango-filtersrelay Let's imagine I have a type FrameType which has an integer field time_offset and I would like to be able to use a range on it - ask only for frames which have the time_offset within the given range.假设我有一个FrameType类型,它有一个整数字段time_offset ,我希望能够在它上面使用一个范围- 只询问具有给定范围内的time_offset帧。 I prepared my schema.py according to the graphene-python docs with a custom FilterSet :我根据带有自定义FilterSetgraphene-python 文档准备了我的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

However, I have no idea how to query the timeOffset field now.但是,我现在不知道如何查询timeOffset字段。 I found no examples online for the django_filters.RangeFilter field.我在网上找不到django_filters.RangeFilter字段的示例。 This is a query I tried:这是我试过的查询:

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

... also with these alternatives: ...还有这些替代方案:

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"

These queries don't raise any error, but they don't filter either (all results are returned).这些查询不会引发任何错误,但它们也不会过滤(返回所有结果)。 I am lost, I'm not sure if I still haven't found the proper syntax, or maybe there's some mistake in my backend code.我迷路了,我不确定我是否还没有找到正确的语法,或者我的后端代码中可能有一些错误。 How should I use and query the django_filters.RangeFilter on a field?我应该如何在字段上使用和查询django_filters.RangeFilter

Sadly, this isn't possible.可悲的是,这是不可能的。 But, there is a workaround for it但是,有一个解决方法

Adjust your filter class as将您的过滤器类调整为

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


class FrameFilter(django_filters.FilterSet):
    time_offset = filters.Filter(method=custom_range_filter_method)

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

Now query the schema with现在查询模式

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

Reference参考

You can handle the range option at Django's queryset level without disturbing the existing relay query.您可以在 Django 的查询集级别处理range选项,而不会干扰现有的中继查询。

In your case,在你的情况下,

  1. Pass start_time_offset and end_time_offset arguments to DjangoConnectionFieldstart_time_offsetend_time_offset参数传递给DjangoConnectionField
  2. Override resolve_frames覆盖resolve_frames
  3. filter on django queryset if start_time_offset or end_time_offset is provided by user else return objects.all()如果start_time_offsetend_time_offset由用户提供,则对 Django 查询集进行过滤,否则返回objects.all()
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()

Now you can query on it with your regular filters provided by relay:现在您可以使用中继提供的常规过滤器对其进行查询:

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

It's a bit old but since it might help others, you can check this thread , it used django filterset for DateRangeFiled and I think you can use a similar approach for integers using filterset RangeFilter .它有点旧,但由于它可能对其他人有帮助,因此您可以检查此线程,它使用 django filterset for DateRangeFiled,我认为您可以使用 filterset RangeFilter对整数使用类似的方法。 Also check this for more info about filterset in graphene.还要检查这个关于石墨烯filterset更多信息。

All you need to do is update your FrameType class as follows:您需要做的就是更新您的 FrameType 类,如下所示:

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

You do not need a custom filterset for this.为此,您不需要自定义过滤器集。 Then you can query as follows:然后你可以查询如下:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM