如何避免Graphene在Django查询响应中对OrderedDict排序?
问题
在Django应用中,我尝试通过Graphene的GenericScalar类型字段返回包含OrderedDict的字典。这些OrderedDict是按值排序的,但API响应里字典会被按键重新排序。
OrderedDict的结构为:{ id : [name, amount] }
发送的OrderedDict示例:
{ 'foods': { 3: ['apple', 5], 1: ['banana', 7], 2: ['carrot', 3] }, 'animals': { 5: ['cat', 3], 3: ['dog', 10], 1: ['pig', 5], } }
API返回的响应:
{ 'foods': { 1: ['banana', 7], 2: ['carrot', 3], 3: ['apple', 5], }, 'animals': { 1: ['pig', 5], 3: ['dog', 10], 5: ['cat', 3], } }
我特意使用OrderedDict来保留顺序,但始终找不到阻止Graphene按键重排的方法。
我的ObjectType和Query定义如下:
class DashboardType(g.ObjectType): data = GenericScalar() class Query(object): dashboard = Field(DashboardDataType, id=ID(), date=Date()) def resolve_dashboard(self, info, **kwargs): data = function_to_get_the_data(kwargs) # 到这一步数据排序都是正确的 return data
直到Graphene发送响应前,数据顺序都符合预期,但响应中顺序丢失了,请问如何实现保留原有顺序?
解决方案
方法1:改用列表嵌套字典替代OrderedDict
JSON规范中对象是无序的,Graphene在序列化时会自动将OrderedDict转换为普通字典,导致顺序丢失。最稳妥的处理方式是将需要保留顺序的结构调整为列表嵌套字典,通过列表明确传递顺序:
调整后的数据结构:
{ 'foods': [ {'id': 3, 'name': 'apple', 'amount': 5}, {'id': 1, 'name': 'banana', 'amount': 7}, {'id': 2, 'name': 'carrot', 'amount': 3} ], 'animals': [ {'id': 5, 'name': 'cat', 'amount': 3}, {'id': 3, 'name': 'dog', 'amount': 10}, {'id': 1, 'name': 'pig', 'amount': 5} ] }
这种方式下,Graphene序列化时会完整保留列表的顺序,前端也能直接按顺序处理数据。
方法2:自定义Scalar类型处理OrderedDict
如果必须保留字典结构,可以自定义一个能识别OrderedDict并保留顺序的Scalar类型:
from graphene.types.scalars import Scalar from graphql.language import ast import json from collections import OrderedDict class OrderedDictScalar(Scalar): @staticmethod def serialize(value): # 对OrderedDict使用sort_keys=False参数序列化,避免按键排序 if isinstance(value, OrderedDict): return json.loads(json.dumps(value, sort_keys=False)) # 其他类型按默认逻辑处理 return value @staticmethod def parse_literal(node): if isinstance(node, ast.StringValue): return json.loads(node.value) return None @staticmethod def parse_value(value): return value
之后在ObjectType中替换原有的GenericScalar为这个自定义类型:
class DashboardType(g.ObjectType): data = OrderedDictScalar()
方法3:重写GenericScalar的序列化逻辑
Graphene的GenericScalar在处理字典时会默认转换为普通dict,导致顺序丢失。可以继承GenericScalar并重写序列化方法:
from graphene.types.generic import GenericScalar from collections import OrderedDict class OrderedGenericScalar(GenericScalar): @staticmethod def serialize(value): if isinstance(value, OrderedDict): # 直接返回OrderedDict,跳过默认的普通dict转换 return value return super().serialize(value)
然后将DashboardType中的data字段类型替换为OrderedGenericScalar即可。
内容的提问来源于stack exchange,提问作者Laura Montes
相关产品推荐
相关产品推荐

