Итак, проблема в том, что graphene.ObjectType не является обычным классом Python. У него есть специальный метакласс, который, как вы видите, реализован здесь. В настоящий момент Python занимается процессом наследования (когда инициализируется сам класс), графен выполняет некоторые операции по регистрации типа. Я не нашел способа изменить типы после того, как произошло наследование. Однако, если вы просто хотите сгенерировать схему из предопределенного шаблона (как у меня) или какого-либо другого источника, вы можете сделать что-то вроде этого. Сначала я определяю удобный метод наследования:
def inherit_from(Child, Parent, persist_meta=False):
"""Return a class that is equivalent to Child(Parent) including Parent bases."""
PersistMeta = copy(Child.Meta) if hasattr(Child, 'Meta') else None
if persist_meta:
Child.Meta = PersistMeta
# Prepare bases
child_bases = inspect.getmro(Child)
parent_bases = inspect.getmro(Parent)
bases = tuple([item for item in parent_bases if item not in child_bases]) + child_bases
# Construct the new return type
try:
Child = type(Child.__name__, bases, Child.__dict__.copy())
except AttributeError as e:
if str(e) == 'Meta':
raise AttributeError('Attribute Error in graphene library. Try setting persist_meta=True in the inherit_from method call.')
raise e
if persist_meta:
Child.Meta = PersistMeta
return Child
Теперь ключевым моментом является выполнение наследования, когда класс типа больше не собирается меняться.
def context_resolver_factory(attr):
"""Create a simple resolver method with default return value None."""
def resolver(obj, info):
return info.context.get(attr, None)
return resolver
class User:
id = graphene.ID()
name = graphene.String(resolver=lambda user, info: user.name)
class Query: pass
me = graphene.Field(User)
def resolve_me(self, info):
return info.context["user"]
inherit_from(User, graphene.ObjectType) # no changes to User class are possible after this line
# method 1: sometimes it's really neat and clean to include a resolver in the field definition
setattr(Query, 'user', graphene.User(resolver=context_resolver_factory('user'))
# or even use lambda if a factory is still overkill
setattr(Query, 'user', graphene.User(resolver=lambda query, info: info.context["user"]))
# method 2: if you want to set the resolver separately, you can do it this way
setattr(Query, 'user', graphene.User())
setattr(Query, 'resolve_user', context_resolver_factory('user'))
# any changes to `Query.Meta` can be done here too
inherit_from(Query, graphene.ObjectType) # no changes to Query class are possible after this line
schema = graphene.Schema(query=Query)
То, что я получил с моей маленькой библиотекой, генерировало все из шаблонного класса, подобного этому (я думаю, вы ищете что-то подобное):
@register_type('Product')
class ProductType:
class Meta:
model = Product
fields = '__all__'
related_fields = {
NestedField('tags', TagType),
NestedField('related_products', 'self'),
}
lookups = {
'id': graphene.ID(),
'name': graphene.String(description="Name"),
'ean': graphene.String(),
'brand': graphene.String()
}
filters = {
'ids': IDFilter,
'django_filter': DjangoFilter,
'pagination': PaginationFilter,
'search_name': ProductMLNSearchFilter
}
Самой большой проблемой были NestedFields и определение автоматического выбора/предварительной выборки запроса Django ORM при поступлении запроса, но я не буду вдаваться в подробности, если это не имеет значения.
person
Jura Brazdil
schedule
18.03.2021