如何动态创建石墨烯对象?例如,我想在运行时根据配置文件添加字段和解析器
Posted
技术标签:
【中文标题】如何动态创建石墨烯对象?例如,我想在运行时根据配置文件添加字段和解析器【英文标题】:How do I dynamically create a graphene Object? For example, I want to at run time add fields and resolvers based off of a configuration file 【发布时间】:2019-10-07 03:49:15 【问题描述】:我希望能够从给定的配置文件动态创建 GraphQL 服务器。因此,例如,我的配置文件将包含应该存在的字段,并且某些字段将带有一个标志,指示它们是主键还是辅助键,这将映射到它们以获取解析器。 如何实现动态创建石墨烯对象类型?
我尝试获取石墨烯的示例代码并向其中添加字段。但它不会接受他们。我尝试深入研究元数据并更新一些选项,但也没有成功。
class User(graphene.ObjectType):
id = graphene.ID()
name = graphene.String()
class Query(graphene.ObjectType):
me = graphene.Field(User)
def resolve_me(self, info):
return info.context["user"]
schema = graphene.Schema(query=Query)
query = """
query something
me
name
temp
"""
if __name__ == "__main__":
userobj = User
print(dir(userobj._meta.fields))
setattr(userobj,"temp", graphene.String())
print((userobj._meta.fields))
userobj._meta.fields.update("temp": graphene.String())
print(userobj._meta.fields)
print((userobj._meta.fields))
result = schema.execute(query, context="user": userobj(id="X", name="Console", temp="hey"))
print(result.data)
print(result.data["me"])
目前我没有通过这次尝试得到任何结果。取出我更新 _meta.fields 的部分,我得到用于创建 userobj 的“temp is not a valid argument”。 'temp' 是用户的无效关键字参数
【问题讨论】:
【参考方案1】:目前正在寻找相同问题的解决方案。有一个可能的answer here
【讨论】:
【参考方案2】:所以,问题在于 graphene.ObjectType 不是一个常规的 Python 类。它有一个特殊的metaclass,你可以看到它实现了here。在 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 查询选择/预取,但除非相关,否则我不会详细介绍。
【讨论】:
以上是关于如何动态创建石墨烯对象?例如,我想在运行时根据配置文件添加字段和解析器的主要内容,如果未能解决你的问题,请参考以下文章
Python GraphQL 如何声明一个自引用的石墨烯对象类型