Django request.session存多会话项触发Product不可JSON序列化报错
问题复现与场景说明
在Django项目request.session中原有字典结构的cart会话项,长期运行无异常。新增整数类型的com_order_id会话项后,程序触发类型错误:
TypeError at ...
Object of type Product is not JSON serializable
触发报错的会话数据样例:
{ 'com_order_id': 59, 'cart': { '3': { 'quantity': 1, 'product': <Product: Christmas With Prestige>, 'total_price': 30000, 'total_weight': Decimal('2.30') } } }
实测验证:仅保留cart会话项、或仅保留com_order_id会话项时,程序均可正常运行;只有两项同时存储时,才会触发上述序列化报错。
核心报错原因
Django默认会话序列化器的分支判断逻辑存在兼容缺陷:
- 序列化器会根据会话内容自动选择存储路径:标准JSON序列化路径仅支持整数、字符串、字典、列表等原生Python类型;Pickle序列化路径支持任意Python对象的序列化
- 默认序列化器的JSON兼容性判断仅校验会话顶层数据的类型,不会递归校验嵌套结构的可序列化性
- 单独存储
cart时,会话顶层值本身是包含不可序列化对象的嵌套结构,序列化器会正确回退到Pickle路径,因此运行正常 - 单独存储
com_order_id整数时,会话全量内容均为JSON兼容的原生类型,直接走JSON序列化路径,因此运行正常 - 两项数据同时存储时,序列化器检测到顶层存在
com_order_id这类JSON兼容的原生类型,就判定整个会话可走JSON序列化路径,没有递归发现cart嵌套结构内的Product模型实例、Decimal类型这类JSON不可序列化对象,直接执行JSON序列化流程,最终触发类型错误。
可行解决方案
- 优化购物车的会话存储结构,不直接存放模型实例
会话存储本身设计为保存轻量可序列化数据,直接存放Product模型实例本身就不符合会话的使用规范。可以将cart内存储的Product实例替换为对应的产品ID,后续业务需要用到产品详情时,再通过ID从数据库查询。同时将Decimal类型的价格、重量值转成整数(以最小货币单位存储价格、以克为单位存储重量),完全适配JSON序列化规则。改造后的购物车结构示例:{ '3': { 'quantity': 1, 'product_id': 3, 'total_price': 30000, # 整数存储,单位为分 'total_weight': 2300 # 整数存储,单位为克 } } - 强制指定会话序列化器为PickleSerializer
在项目settings.py中补充配置,强制全量会话走Pickle序列化路径,绕过默认的自动分支判断逻辑,适配任意Python对象的存储需求:
需注意该方案的安全风险:Pickle序列化的数据如果被恶意篡改,可能触发反序列化漏洞,仅适合内网、可控场景使用。SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' - 自定义带异常捕获的会话序列化器
基于默认的序列化逻辑做扩展,在JSON序列化执行失败时自动捕获异常并回退到Pickle序列化,兼容两类数据的混合存储场景。
内容的提问来源于stack exchange,提问作者ksh
相关产品推荐
相关产品推荐

