在Django中存储含float.inf的元组列表至PostgreSQL JSONB字段
解决包含Infinity的元组列表存入PostgreSQL JSONB字段的问题
你遇到的问题核心在于JSON标准本身不支持Infinity和NaN,Python默认的JSONEncoder会把float.inf序列化为字符串"Infinity",但PostgreSQL的jsonb类型不识别这个标记,因此抛出了语法错误。下面是一套简单可行的序列化/反序列化方案:
1. 自定义JSON编码器处理特殊值
我们需要继承JSON编码器,把inf、-inf、NaN转换成JSON兼容的字符串标记,同时把元组转为列表(因为JSON没有元组类型):
import json from django.core.serializers.json import DjangoJSONEncoder # 继承Django默认编码器,保留对datetime等Django常用类型的支持 class MathJSONEncoder(DjangoJSONEncoder): def default(self, obj): # 处理浮点特殊值 if isinstance(obj, float): if obj == float('inf'): return "inf" elif obj == float('-inf'): return "-inf" elif obj != obj: # 判断是否为NaN return "nan" # 把元组转为列表(JSON仅支持数组) elif isinstance(obj, tuple): return list(obj) # 其他类型交给父类处理 return super().default(obj)
2. 在Django模型中指定编码器
直接在JSONField里配置我们自定义的编码器,这样保存数据时会自动处理:
from django.db import models class YourModel(models.Model): a_math_function = models.JSONField(encoder=MathJSONEncoder) # 其他字段...
现在你可以正常保存包含float.inf的元组列表了:
data = [(float.inf, 1.0), (270, 0.9002), (0, 0.0)] YourModel.objects.create(a_math_function=data)
此时存入数据库的JSON数据会是:[["inf", 1.0], [270, 0.9002], [0, 0.0]],完全符合PostgreSQL jsonb的语法要求。
3. 反序列化还原原始数据
从数据库取出数据时,需要把字符串标记转回对应的浮点特殊值,同时把列表还原为元组。我们可以写一个自定义解码器:
def math_json_decoder(obj): # 处理数组(原元组转成的列表) if isinstance(obj, list): processed_items = [] for item in obj: # 递归处理嵌套元素 processed = math_json_decoder(item) # 如果是嵌套列表(原元组),转回元组 if isinstance(item, list) and len(item) == 2: # 根据你的元组结构调整 processed_items.append(tuple(processed)) else: processed_items.append(processed) return processed_items # 处理字符串标记,转回特殊浮点值 elif isinstance(obj, str): if obj == "inf": return float('inf') elif obj == "-inf": return float('-inf') elif obj == "nan": return float('nan') else: return obj # 其他类型直接返回 else: return obj
使用方式:
import json # 取出数据 instance = YourModel.objects.get(pk=1) # 反序列化还原原始结构 original_data = json.loads(instance.a_math_function, object_hook=math_json_decoder) # original_data 会是:[(inf, 1.0), (270, 0.9002), (0, 0.0)]
额外说明
- 如果你不需要兼容Django的特殊类型(比如datetime),可以直接继承标准库的
json.JSONEncoder,不用DjangoJSONEncoder。 - 第三方库如
simplejson虽然支持直接序列化inf,但PostgreSQL的jsonb仍然会拒绝这类值,所以转字符串标记是最稳妥的方案。
内容的提问来源于stack exchange,提问作者comiventor
相关产品推荐
相关产品推荐

