简体   繁体   English

如何使用graphene-django通过GraphQL中的id列表过滤查询?

[英]How to filter a query by a list of ids in GraphQL using graphene-django?

I'm trying to perform a GraphQL query using Django and Graphene.我正在尝试使用 Django 和 Graphene 执行 GraphQL 查询。 To query one single object using the id I did the following:要使用 id 查询单个对象,我执行了以下操作:

{
  samples(id:"U2FtcGxlU2V0VHlwZToxMjYw") {
    edges {
      nodes {
        name
      }
    }
  }
}

And it just works fine.它工作正常。 Problem arise when I try to query with more than one id, like the following:当我尝试使用多个 id 进行查询时出现问题,如下所示:

{
  samples(id_In:"U2FtcGxlU2V0VHlwZToxMjYw, U2FtcGxlU2V0VHlwZToxMjYx") {
    edges {
      nodes {
        name
      }
    }
  }
} 

In the latter case I got the following error:在后一种情况下,我收到以下错误:

argument should be a bytes-like object or ASCII string, not 'list'

And this is a sketch of how defined the Type and Query in django-graphene这是如何在django-graphene定义类型和查询的草图

class SampleType(DjangoObjectType):
  class Meta:
    model = Sample
    filter_fields = {
      'id': ['exact', 'in'],
     }
     interfaces = (graphene.relay.Node,)

class Query(object):
  samples = DjangoFilterConnectionField(SampleType)

  def resolve_sample_sets(self, info, **kwargs):
    return Sample.objects.all()

GlobalIDMultipleChoiceFilter from django-graphene kinda solves this issue, if you put "in" in the field name.来自 django-graphene 的GlobalIDMultipleChoiceFilter有点解决这个问题,如果你在字段名称中输入“in”。 You can create filters like您可以创建过滤器,例如

from django_filters import FilterSet
from graphene_django.filter import GlobalIDMultipleChoiceFilter

class BookFilter(FilterSet):
    author = GlobalIDMultipleChoiceFilter()

and use it by并使用它

{
  books(author: ["<GlobalID1>", "<GlobalID2>"]) {
    edges {
      nodes {
        name
      }
    }
  }
}

Still not perfect, but the need for custom code is minimized.仍然不完美,但对自定义代码的需求已最小化。

I had trouble implementing the 'in' filter as well--it appears to be misimplemented in graphene-django right now and does not work as expected.我在实现“in”过滤器时也遇到了麻烦——它现在似乎在 graphene-django 中被错误实现,并且没有按预期工作。 Here are the steps to make it work:以下是使其工作的步骤:

  1. Remove the 'in' filter from your filter_fields从 filter_fields 中删除“in”过滤器
  2. Add an input value to your DjangoFilterConnectionField entitled 'id__in' and make it a list of IDs将输入值添加到名为“id__in”的 DjangoFilterConnectionField 并使其成为 ID 列表
  3. Rename your resolver to match the 'samples' field.重命名您的解析器以匹配“样本”字段。
  4. Handle filtering by 'id__in' in your resolver for the field.在您的解析器中处理字段的“id__in”过滤。 For you this will look as follows:对您来说,这将如下所示:
from base64 import b64decode

def get_pk_from_node_id(node_id: str):
    """Gets pk from node_id"""
    model_with_pk = b64decode(node_id).decode('utf-8')
    model_name, pk = model_with_pk.split(":")
    return pk


class SampleType(DjangoObjectType):
    class Meta:
        model = Sample
        filter_fields = {
            'id': ['exact'],
         }
        interfaces = (graphene.relay.Node,)


class Query(object):

    samples = DjangoFilterConnectionField(SampleType, id__in=graphene.List(graphene.ID))

    def resolve_samples(self, info, **kwargs):
        # filter_field for 'in' seems to not work, this hack works
        id__in = kwargs.get('id__in')
        if id__in:
            node_ids = kwargs.pop('id__in')
            pk_list = [get_pk_from_node_id(node_id) for node_id in node_ids]
            return Sample._default_manager.filter(id__in=pk_list)
        return Sample._default_manager.all()

This will allow you to call the filter with the following api.这将允许您使用以下 api 调用过滤器。 Note the use of an actual array in the signature (I think this is a better API than sending a comma separated string of values).注意签名中实际数组的使用(我认为这是一个比发送逗号分隔的值字符串更好的 API)。 This solution still allows you to add other filters to the request and they will chain together correctly.此解决方案仍然允许您向​​请求添加其他过滤器,它们将正确链接在一起。

{
  samples(id_In: ["U2FtcGxlU2V0VHlwZToxMjYw", "U2FtcGxlU2V0VHlwZToxMjYx"]) {
    edges {
      nodes {
        name
      }
    }
  }
} 

You can easily use a Filter just put this with your nodes.您可以轻松地使用过滤器,只需将其与您的节点放在一起即可。

class ReportFileFilter(FilterSet):
    id = GlobalIDMultipleChoiceFilter()

Then in your query just use -然后在您的查询中使用 -

class Query(graphene.ObjectType):
    all_report_files = DjangoFilterConnectionField(ReportFileNode, filterset_class=ReportFileFilter)

This is for relay implementation of graphql django.这是用于 graphql django 的中继实现。

None of the existing answers seemed to work for me as they were presented, however with some slight changes I managed to resolve my problem as follows:现有的答案似乎都不适合我,但是通过一些细微的更改,我设法解决了我的问题,如下所示:

You can create a custom FilterSet class for your object type, and filter the field by using the GlobalIDMultipleChoiceFilter .您可以为您的对象类型创建自定义FilterSet类,并使用GlobalIDMultipleChoiceFilter过滤字段。 for example:例如:

from django_filters import FilterSet
from graphene_django.filter import GlobalIDFilter, GlobalIDMultipleChoiceFilter

class SampleFilter(FilterSet):
    id = GlobalIDFilter()
    id__in = GlobalIDMultipleChoiceFilter(field_name="id")

    class Meta:
        model = Sample
        fields = (
            "id_in",
            "id",
        )

Something I came cross is that you can not have filter_fields defined with this approach .我遇到的问题是您不能使用这种方法定义 filter_fields Instead, you have to only rely on the custom FilterSet class exclusively, making your object type effectively look like this:相反,您只需要专门依赖自定义FilterSet类,使您的对象类型有效地如下所示:

from graphene import relay
from graphene_django import DjangoObjectType

class SampleType(DjangoObjectType):
    class Meta:
        model = Sample
        filterset_class = SampleFilter
        interfaces = (relay.Node,)

Another way is to tell the Relay filter of graphene_django to also deals with a list.另一种方法是告诉 graphene_django 的 Relay 过滤器也处理一个列表。 This filter is register in a mixin in graphene_django and applied to any filter you define.该过滤器注册在 graphene_django 的 mixin 中,并应用于您定义的任何过滤器。

So here my solution:所以这里我的解决方案:

from graphene_django.filter.filterset import (
    GlobalIDFilter,
    GrapheneFilterSetMixin,
)
from graphql_relay import from_global_id


class CustomGlobalIDFilter(GlobalIDFilter):
    """Allow __in lookup for IDs"""
    def filter(self, qs, value):
        if isinstance(value, list):
            value_lst = [from_global_id(v)[1] for v in value]
            return super(GlobalIDFilter, self).filter(qs, value_lst)
        else:
            return super().filter(qs, value)

# Fix the mixin defaults
GrapheneFilterSetMixin.FILTER_DEFAULTS.update({
    AutoField: {"filter_class": CustomGlobalIDFilter},
    OneToOneField: {"filter_class": CustomGlobalIDFilter},
    ForeignKey: {"filter_class": CustomGlobalIDFilter},
})

暂无
暂无

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

相关问题 如何过滤在graphene-django中为空的石墨烯标量字段? - How to filter a graphene scalar field that is null in graphene-django? 如何在graphene-django中使用MultipleChoiceFilter? - How to use MultipleChoiceFilter in graphene-django? 使用graphene-django,是否可以定义两个节点之间的循环关系? - Using graphene-django, is it possible to define a circular relationship between two nodes? graphene-django 为 DjangoFilterConnectionField 添加附加参数并将其传递给 get_queryset - graphene-django add additional argument for DjangoFilterConnectionField and pass it to get_queryset 如何使用Django-Filter执行或查询 - How to perform Or Query using Django-Filter 如何在Django中按查询列表的顺序获取过滤结果 - How to get filter result in the order of query list in Django graphene-django: 没有 &#39;Meta.fields&#39; 或 &#39;Meta.exclude&#39; 的 &#39;Meta.model&#39; 自 0.15.0 起已被弃用,现在不允许使用 - graphene-django: 'Meta.model' without either 'Meta.fields' or 'Meta.exclude' has been deprecated since 0.15.0 and is now disallowed 如何使用允许通过一个 model 字段的多个值查询的 django-filter 构建过滤器 class - How to build filter class using django-filter that allows to query by multiple values of one model field 如何在用字典列表而不是查询集填充后过滤 django-tables2? - How to filter django-tables2 after populating it with list of dicts rather than a query set? 如何为从下拉列表中选择的多个值编写 Django 过滤器查询? - How to write a django filter query for multiple values selected from a dropdown list?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM