Django Graphene中基于REST API实现Device与Installation关联查询问题
我正在用Django Graphene构建GraphQL服务器,数据源是一个RESTful API,目前的Schema代码如下:
class DeviceType(graphene.ObjectType): id = graphene.ID() reference = graphene.String() serial = graphene.Float() class InstallationType(graphene.ObjectType): id = graphene.ID() company = graphene.Int() device = graphene.ID() class AllDataType(graphene.ObjectType): device = graphene.Field(DeviceType) installation = graphene.Field(InstallationType) class Query(graphene.ObjectType): installation = graphene.Field( InstallationType, device_id=graphene.Int(required=True) ) all = graphene.Field( AllDataType, serial=graphene.Float(required=True) ) def resolve_installation(self, info, device_id): response = api_call('installations/?device=%s' % device_id)['results'][0] return json2obj(json.dumps(response)) def resolve_all(self, info, serial): response = api_call('devices/?serial=%s' % serial)['results'][0] return json2obj(json.dumps(response))
我需要执行这样的查询:
query { all(serial:201002000856){ device{ id serial reference } installation{ company device } } }
现在遇到的问题是:怎么在AllDataType里建立DeviceType和InstallationType的关联?resolve_installation需要传入设备ID,而resolve_all是通过设备序列号获取Device数据的,我想利用resolve_all返回的Device ID来解析对应的Installation,该怎么实现?
其实这事很简单,你只需要给AllDataType添加一个对应的resolve_installation方法就搞定了。在Graphene里,字段的解析函数可以直接定义在它的父类型上,这样就能拿到父对象(也就是AllDataType的实例)里已经获取到的device数据,进而用device ID去调用API拿对应的Installation信息。
具体改法看这里:
- 给
AllDataType添加解析安装信息的方法:
class AllDataType(graphene.ObjectType): device = graphene.Field(DeviceType) installation = graphene.Field(InstallationType) def resolve_installation(self, info): # 这里的self就是resolve_all返回的AllDataType实例,已经包含device的数据了 device_id = self.device.id # 直接复用你之前写好的API调用逻辑就行 response = api_call('installations/?device=%s' % device_id)['results'][0] return json2obj(json.dumps(response))
- 确认下你的
resolve_all返回的对象里确实包含device的id——从你现在的代码来看,resolve_all是从devices/?serial=xxx接口拿数据,这个接口返回的device对象肯定带id,所以这一步没问题。
这样当你执行目标查询时,Graphene会先跑resolve_all拿到AllDataType的实例,然后解析installation字段的时候,就会调用上面写的resolve_installation方法,用已经拿到的device ID去请求对应的安装信息,完美把device和installation关联起来。
另外还有一种思路:如果以后InstallationType可能在别的场景下也需要通过device ID解析,你也可以把解析逻辑写在InstallationType本身,但这种方式需要传递device ID到上下文或者依赖父对象属性,不如第一种方法直接贴合你当前的需求。比如:
class InstallationType(graphene.ObjectType): id = graphene.ID() company = graphene.Int() device = graphene.ID() def resolve_installation(self, info): # 这种方式需要提前把device_id放到上下文里,不如第一种直接 device_id = info.context.get('device_id') response = api_call('installations/?device=%s' % device_id)['results'][0] return json2obj(json.dumps(response))
但还是第一种方法最适合你现在的场景,逻辑清晰,不需要额外处理上下文,直接用已经拿到的device数据就行。
内容的提问来源于stack exchange,提问作者cardoso

