如何为GraphQL ObjectType动态添加字段列表?
动态给Graphene ObjectType添加字段的解决方案
问题背景
你尝试通过setattr直接给继承自graphene.ObjectType的MetaDataObject添加动态字段,但因为Graphene的元类机制,类定义阶段元类就已完成字段扫描和处理,后续直接设置类属性不会被Graphene的Schema识别。
解决方案1:动态创建ObjectType类(推荐)
直接通过Python的type()函数,将静态字段和动态字段合并后动态生成MetaDataObject类。这种方法让Graphene的元类在类创建时完整处理所有字段,规避元类机制的限制。
from starlette_graphene3 import GraphQLApp import graphene from fastapi import APIRouter, Depends # 假设auth_middleware和table已提前定义 auth_middleware = lambda: None table = type('MockTable', (), { 'get_item': lambda self, Key: { 'Item': { 'resource_type': 'book', 'resource_name': '1984', 'length': 328, 'authors': ['George Orwell'], 'deprecated': 'false' } } })() app = APIRouter( dependencies=[Depends(auth_middleware)], ) DYNAMIC_TYPES = { "length": graphene.Int(), # 注意:原代码的of_type=str需改为graphene.String(),Graphene列表要求接收Graphene类型 "authors": graphene.List(graphene.String()), "deprecated": graphene.String() } # 合并静态字段与动态字段 static_fields = { "resource_type": graphene.String(), "resource_name": graphene.String() } all_fields = {**static_fields, **DYNAMIC_TYPES} # 动态生成MetaDataObject类 MetaDataObject = type('MetaDataObject', (graphene.ObjectType,), all_fields) class Query(graphene.ObjectType): get_metadata = graphene.Field(MetaDataObject, resource_type=graphene.String(), resource_name=graphene.String()) def resolve_get_metadata(self, info, resource_type, resource_name): response = table.get_item( Key={ 'resource_type': resource_type, 'resource_name': resource_name } ) if 'Item' not in response: return None return response['Item'] schema = graphene.Schema(query=Query, auto_camelcase=False) app.add_route("/graphql", GraphQLApp(schema=schema))
解决方案2:手动更新元类的字段存储
如果必须先定义MetaDataObject类再添加动态字段,可以直接修改类的_meta.fields字典(Graphene元类存储字段的位置),同时设置类属性:
from starlette_graphene3 import GraphQLApp import graphene from fastapi import APIRouter, Depends auth_middleware = lambda: None table = type('MockTable', (), { 'get_item': lambda self, Key: { 'Item': { 'resource_type': 'book', 'resource_name': '1984', 'length': 328, 'authors': ['George Orwell'], 'deprecated': 'false' } } })() app = APIRouter( dependencies=[Depends(auth_middleware)], ) DYNAMIC_TYPES = { "length": graphene.Int(), "authors": graphene.List(graphene.String()), "deprecated": graphene.String() } class MetaDataObject(graphene.ObjectType): resource_type = graphene.String() resource_name = graphene.String() # 手动更新元类的字段字典,并设置类属性 for attr, g_type in DYNAMIC_TYPES.items(): MetaDataObject._meta.fields[attr] = g_type setattr(MetaDataObject, attr, g_type) class Query(graphene.ObjectType): get_metadata = graphene.Field(MetaDataObject, resource_type=graphene.String(), resource_name=graphene.String()) def resolve_get_metadata(self, info, resource_type, resource_name): response = table.get_item( Key={ 'resource_type': resource_type, 'resource_name': resource_name } ) if 'Item' not in response: return None return response['Item'] schema = graphene.Schema(query=Query, auto_camelcase=False) app.add_route("/graphql", GraphQLApp(schema=schema))
注意事项
- 原代码中
graphene.List(of_type=str)是错误写法,Graphene的List需要接收Graphene类型(如graphene.String()),而非Python原生类型。 - 方案1更稳妥,避免直接修改元类内部属性可能带来的版本兼容性问题。
内容的提问来源于stack exchange,提问作者Jayjay Jay
相关产品推荐
相关产品推荐

