7

我正在尝试使用 Django 和 Graphene 执行 GraphQL 查询。要使用 id 查询单个对象,我执行了以下操作:

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

它工作正常。当我尝试使用多个 id 进行查询时会出现问题,如下所示:

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

在后一种情况下,我收到以下错误:

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

这是如何定义类型和查询的草图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()
4

5 回答 5

5

GlobalIDMultipleChoiceFilter from django-graphene kinda solves this issue, if you put "in" in the field name. 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.

于 2020-03-21T05:11:16.873 回答
2

现有的答案似乎都不适合我,但是通过一些细微的改变,我设法解决了我的问题,如下所示:

您可以为您的对象类型创建一个自定义FilterSet类,并使用GlobalIDMultipleChoiceFilter. 例如:

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",
        )

我遇到的问题是您不能使用这种方法定义 filter_fields。相反,您必须只依赖自定义FilterSet类,使您的对象类型有效地看起来像这样:

from graphene import relay
from graphene_django import DjangoObjectType

class SampleType(DjangoObjectType):
    class Meta:
        model = Sample
        filterset_class = SampleFilter
        interfaces = (relay.Node,)
于 2020-06-07T14:44:35.777 回答
1

您可以轻松地使用过滤器,只需将其与您的节点一起使用。

class ReportFileFilter(FilterSet):
    id = GlobalIDMultipleChoiceFilter()

然后在您的查询中使用 -

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

这是用于 graphql django 的中继实现。

于 2020-05-03T03:58:34.957 回答
1

我在实现“in”过滤器时也遇到了麻烦——它现在似乎在 graphene-django 中被错误地实现并且不能按预期工作。以下是使其工作的步骤:

  1. 从您的 filter_fields 中删除“in”过滤器
  2. 将输入值添加到名为“id__in”的 DjangoFilterConnectionField 并使其成为 ID 列表
  3. 重命名您的解析器以匹配“样本”字段。
  4. 在字段的解析器中按“id__in”处理过滤。对你来说,这将如下所示:
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()

这将允许您使用以下 api 调用过滤器。请注意在签名中使用实际数组(我认为这是一个比发送逗号分隔的值字符串更好的 API)。此解决方案仍允许您将其他过滤器添加到请求中,它们将正确链接在一起。

{
  samples(id_In: ["U2FtcGxlU2V0VHlwZToxMjYw", "U2FtcGxlU2V0VHlwZToxMjYx"]) {
    edges {
      nodes {
        name
      }
    }
  }
} 
于 2019-08-13T22:53:19.113 回答
0

另一种方法是告诉 graphene_django 的 Relay 过滤器也处理一个列表。此过滤器在 graphene_django 的 mixin 中注册,并应用于您定义的任何过滤器。

所以这里是我的解决方案:

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},
})
于 2020-11-06T16:53:47.767 回答