Как отфильтровать запрос по списку идентификаторов в GraphQL с помощью graphene-django? - PullRequest
0 голосов
/ 16 января 2019

Я пытаюсь выполнить запрос GraphQL, используя Django и Graphene. Для запроса одного объекта с использованием идентификатора я сделал следующее:

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

И это просто отлично работает. Проблема возникает, когда я пытаюсь сделать запрос с более чем одним идентификатором, например:

{
  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()

1 Ответ

0 голосов
/ 14 августа 2019

У меня также были проблемы с реализацией фильтра «in» - он, похоже, неправильно реализован в графене-джанго сейчас и работает не так, как ожидалось. Вот шаги, чтобы заставить это работать:

  1. Удалите фильтр 'in' из ваших полей фильтра
  2. Добавьте входное значение к вашему DjangoFilterConnectionField с именем 'id__in' и сделайте его списком идентификаторов
  3. Переименуйте ваш резольвер, чтобы он соответствовал полю 'samples'.
  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
      }
    }
  }
} 
...