Django Graphene возвращает данные из нескольких моделей под одним гнездом

Я пытаюсь использовать Python Graphene GraphQL для реализации конечной точки поиска, возвращающей все продукты на основе названия. Однако в моей базе данных есть три таблицы продуктов, которые содержат различные типы продуктов - карты, токены, запечатанные продукты.

Я хочу вернуть данные в одном гнезде в Json-ответе. Релейное соединение я использую с https://github.com/saltycrane/graphene-relay-pagination-example/blob/artsy-example/README.md.

Что-то вроде:

Код:

class MagicCards(DjangoObjectType):
    id = graphene.ID(source='pk', required=True)
    mana_cost_list = graphene.List(graphene.String)

    class Meta:
        model = magic_sets_cards
        interfaces = (relay.Node,)
        filter_fields = {'name': ['icontains']}
        connection_class = ArtsyConnection


class MagicTokens(DjangoObjectType):
    id = graphene.ID(source='pk', required=True)

    class Meta:
        model = magic_sets_tokens
        interfaces = (relay.Node,)
        filter_fields = {'name': ['icontains']}
        connection_class = ArtsyConnection


class SearchQuery(ObjectType):
    magic_cards = ArtsyConnectionField(MagicCards)
    magic_tokens = ArtsyConnectionField(MagicTokens)
    # pseudo code:
    all_products = combine(magic_cards, magic_tokens)

    @staticmethod
    def resolve_all_products(self, info, **kwargs):
        return

    @staticmethod
    def resolve_magic_cards(self, info, **kwargs):
        sql_number_to_int = "CAST((REGEXP_MATCH(number, '\d+'))[1] as INTEGER)"
        excluded_sides = ['b', 'c', 'd', 'e']
        return magic_sets_cards.objects.exclude(side__in=excluded_sides).extra(select={'int': sql_number_to_int}).order_by('-set_id__release_date', 'set_id__name', 'int', 'number').all()

    @staticmethod
    def resolve_magic_tokens(self, info, **kwargs):
        sql_number_to_int = "CAST((REGEXP_MATCH(number, '\d+'))[1] as INTEGER)"
        excluded_sides = ['b', 'c', 'd', 'e']
        return magic_sets_tokens.objects.exclude(side__in=excluded_sides).extra(select={'int': sql_number_to_int}).order_by('-set_id__release_date', 'set_id__name', 'int', 'number').all()


searchSchema = graphene.Schema(query=SearchQuery)

Query:

{
  allProducts(name_Icontains: "Spellbook", first: 12, after: "") {
    pageCursors {
      previous {
        cursor
      }
      first {
        cursor
        page
      }
      around {
        cursor
        isCurrent
        page
      }
      last {
        cursor
        page
      }
      next {
        cursor
      }
    }
    edges {
      node {
        ... on MagicCards {
          name
        }
        ... on MagicTokens {
          name
        }
      }
    }
  }
}

Теперь я мог бы сделать следующий запрос, однако это означало бы, что каждый тип продукта будет находиться в отдельном гнезде в Json-ответе с собственными курсорами страниц, что мне не нужно.

{
  magicCards(name_Icontains: "Spellbook", first: 12, after: "") {
    pageCursors {
      ...
    }
    edges {
      node {
        name
      }
    }
  }
  magicTokens(name_Icontains: "Spellbook", first: 12, after: "") {
    pageCursors {
      ...
    }
    edges {
      node {
        name
      }
    }
  }
}

Вам необходимо использовать тип Union. Попробуйте следующее:

class MagicCards(DjangoObjectType):
    id = graphene.ID(source='pk', required=True)
    mana_cost_list = graphene.List(graphene.String)

    class Meta:
        model = magic_sets_cards
        interfaces = (relay.Node,)


class MagicTokens(DjangoObjectType):
    id = graphene.ID(source='pk', required=True)

    class Meta:
        model = magic_sets_tokens
        interfaces = (relay.Node,)
      

class SearchType(graphene.Union):
    class Meta:
        types = (MagicCards, MagicTokens)

class SearchConnection(graphene.Connection):
    class Meta:
        node = SearchType


class SearchQuery(ObjectType):
    all_products = graphene.ConnectionField(SearchConnection, name__icontains=String())

    @staticmethod
    def resolve_all_products(self, info, **kwargs):
        # do filtering with kwargs['name__icontains']
        sql_number_to_int = "CAST((REGEXP_MATCH(number, '\d+'))[1] as INTEGER)"
        excluded_sides = ['b', 'c', 'd', 'e']
        items = list( magic_sets_cards.objects.exclude(side__in=excluded_sides).extra(select={'int': sql_number_to_int}).order_by('-set_id__release_date', 'set_id__name', 'int', 'number').all())
        items.extend(magic_sets_tokens.objects.exclude(side__in=excluded_sides).extra(select={'int': sql_number_to_int}).order_by('-set_id__release_date', 'set_id__name', 'int', 'number').all())
        return items

    



searchSchema = graphene.Schema(query=SearchQuery)

Вы должны избегать DjangoConnectionField или DjangoFilterConnectionField, поскольку они не принимают типы Union. Логика фильтрации должна быть реализована в логине, что вы можете легко сделать с помощью django-filter. Возвращаемый pageInfo объект будет startCursor, endCursor, hasNextPage, hasPreviousPage по умолчанию. Мне нужно будет увидеть ваш класс ArtsyConnection, чтобы настроить его.

https://docs.graphene-python.org/en/latest/types/unions/

Вернуться на верх