Тип объединения не может разрешить тип объекта во время выполнения

Я настраиваю сервер GraphQL с Python, используя Starlette и Graphene, и столкнулся с проблемой, для которой не могу найти решения. Документация Графен не вдаваться в подробности относительно типа объединения, который я пытаюсь реализовать. Я установил минимальный пример на основе документации по графену, которую вы можете запустить, чтобы воспроизвести эту проблему.

import os

import uvicorn
from graphene import ObjectType, Field, List, String, Int, Union
from graphene import Schema
from starlette.applications import Starlette
from starlette.graphql import GraphQLApp
from starlette.routing import Route

mock_data = {
    "episode": 3,
    "characters": [
        {
            "type": "Droid",
            "name": "R2-D2",
            "primaryFunction": "Astromech"
        },
        {
            "type": "Human",
            "name": "Luke Skywalker",
            "homePlanet": "Tatooine"
        },
        {
            "type": "Starship",
            "name": "Millennium Falcon",
            "length": 35
        }
    ]
}


class Human(ObjectType):
    name = String()
    homePlanet = String()


class Droid(ObjectType):
    name = String()
    primary_function = String()


class Starship(ObjectType):
    name = String()
    length = Int()


class Characters(Union):
    class Meta:
        types = (Human, Droid, Starship)


class SearchResult(ObjectType):
    characters = List(Characters)
    episode = Int()


class RootQuery(ObjectType):
    result = Field(SearchResult)

    @staticmethod
    def resolve_result(_, info):
        return mock_data


graphql_app = GraphQLApp(schema=Schema(query=RootQuery))

routes = [
    Route("/graphql", graphql_app),
]

api = Starlette(routes=routes)

if __name__ == "__main__":
    uvicorn.run(api, host="127.0.0.1", port=int(os.environ.get("PORT", 8080)))

Если вы затем перейдете на http://localhost:8080/graphq и введите следующий запрос

query Humans{
  result {
    episode
    characters {
      ... on Human {
        name
      }
    }
  }
}

Я получаю эту ошибку

 {
    "data": {
        "result": {
            "episode": 3,
            "characters": null
        }
    },
    "errors": [
        {
            "message": "Abstract type Characters must resolve to an Object type at runtime for field SearchResult.characters with value \"[{'type': 'Droid', 'name': 'R2-D2', 'primaryFunction': 'Astromech'}, {'type': 'Human', 'name': 'Luke Skywalker', 'homePlanet': 'Tatooine'}, {'type': 'Starship', 'name': 'Millennium Falcon', 'length': 35}]\", received \"None\".",
            "locations": [
                {
                    "line": 4,
                    "column": 5
                }
            ]
        }
    ]
}

с которым я сейчас застрял. Может кто это уже делал и может выручит? Как я могу решить эту проблему во время выполнения. Я уже пробовал разные подходы, например, я изменил классы Character и RootQuery:

class Character(Union):
    class Meta:
        types = (Human, Droid, Starship)

    def __init__(self, data, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.data = data
        self.type = data.get("type")

    def resolve_type(self, info):
        if self.type == "Human":
            return Human
        if self.type == "Droid":
            return Droid
        if self.type == "Starship":
            return Starship


class RootQuery(ObjectType):
    result = Field(SearchResult)
    
    @staticmethod
    def resolve_result(_, info):
        return {**mock_data, "characters": [Character(character) for character in mock_data.get('characters')]}

в результате чего

{
    "data": {
        "result": {
            "episode": 3,
            "characters": [
                {},
                {
                    "name": null
                },
                {}
            ]
        }
    }
}

Любые идеи были бы очень признательны!

1 ответ

Jkimbo ответил на вопрос здесь:

class Character(Union):
    class Meta:
        types = (Human, Droid, Starship)

    @classmethod
    def resolve_type(cls, instance, info):
        if instance["type"] == "Human":
            return Human
        if instance["type"] == "Droid":
            return Droid
        if instance["type"] == "Starship":
            return Starship


class RootQuery(ObjectType):
    result = Field(SearchResult)

    def resolve_result(_, info):
        return mock_data

Обратите внимание: я просто возвращаю mock_data и обновил метод resolve_type, чтобы переключаться на основе данных. Тип Union использует тот же метод resolve_type, что и Interface, чтобы выяснить, какой тип разрешать во время выполнения: https://docs.graphene-python.org/en/latest/types/interfaces/

Другие вопросы по тегам