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

ilm*_*rez 7 django-filter graphene-python graphql-python

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

{
  samples(id:"U2FtcGxlU2V0VHlwZToxMjYw") {
    edges {
      nodes {
        name
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

{
  samples(id_In:"U2FtcGxlU2V0VHlwZToxMjYw, U2FtcGxlU2V0VHlwZToxMjYx") {
    edges {
      nodes {
        name
      }
    }
  }
} 
Run Code Online (Sandbox Code Playgroud)

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

argument should be a bytes-like object or ASCII string, not 'list'
Run Code Online (Sandbox Code Playgroud)

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

ulg*_*ens 5

GlobalIDMultipleChoiceFilter从 django-graphene 有点解决这个问题,如果你在字段名称中输入“in”。您可以创建过滤器,例如

from django_filters import FilterSet
from graphene_django.filter import GlobalIDMultipleChoiceFilter

class BookFilter(FilterSet):
    author = GlobalIDMultipleChoiceFilter()
Run Code Online (Sandbox Code Playgroud)

并使用它

{
  books(author: ["<GlobalID1>", "<GlobalID2>"]) {
    edges {
      nodes {
        name
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

仍然不完美,但对自定义代码的需求已最小化。


Col*_*cks 2

我在实现“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()
Run Code Online (Sandbox Code Playgroud)

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

{
  samples(id_In: ["U2FtcGxlU2V0VHlwZToxMjYw", "U2FtcGxlU2V0VHlwZToxMjYx"]) {
    edges {
      nodes {
        name
      }
    }
  }
} 
Run Code Online (Sandbox Code Playgroud)