避坑说明
在使用 Prisma Python 处理 JSON 列时，如果直接把 Python 的 dict/list 结构交给 CRUD 方法，Prisma 会“聪明”地把这些结构内联到 GraphQL mutation 文本中。这一行为在键名包含连字符、空格、首字母大写或任何 GraphQL 不合法字符时，会把键拆成多个字段，从而生成类似 Authorization-Type 这样的片段；GraphQL 引擎会把它误读为浮点数字面量或非法 token，最终抛出 “unsupported float”-Type” 一类语法错误。由于这类错误发生在 Prisma 引擎解析阶段，FastAPI/Starlette 只会看到一个笼统的 500，容易让人误以为是数据库访问失败或 JSON Schema 校验问题。

触发条件：向任意 Prisma JSON 字段（Json 类型列）写入的字典/数组中存在连字符、空格、冒号、首字母大写等特殊键名；Prisma 将其内联展开而非通过变量传递。
典型表现：API 层收到 500；日志中出现 prisma.errors.DataError，提示 Error parsing GraphQL query，伴随 unsupported float、Expected ':' 等语句；数据未落库。
常见误判：误认为 JSON Schema 校验失败、PostgreSQL JSONB 不支持、或请求体编码问题；实际上问题发生在 GraphQL query 构造阶段，与数据库无关。
这一陷阱在对接外部 API、配置灵活的 Header/Cookie placement、或存储第三方响应映射时极易踩中。为此需要明确 Prisma Python 的一个关键特性：它提供了 prisma.Json 类型（prisma._fields.Json），只要用 Json(value) 包裹 dict/list，Prisma 就会把该值作为 GraphQL 变量传输而非直接拼接到 query 字面量中。变量传输不会破坏键名，自然也不会触发 GraphQL 语法错误。

通用规约

包装策略：给所有写入 JSON 列的值（包括嵌套对象、数组）执行一次统一封装，典型实现是 _to_prisma_json(value)：若是 dict/list 则返回 Json(value)，否则原样返回。
适用场景：创建/更新 JSON 列（create、update、upsert）、执行事务内的 JSON 写操作、批量操作中的 JSON payload、手动调用 client.query_raw() 带 JSON 变量时也建议同样处理。
位置覆盖：不仅限于单个入口函数，凡是通过服务层再回写数据库的流程（例如自动登录成功后用响应更新 Secret.data、记录字段来源、存储登录表单）都需要使用封装，避免后续新增字段时再次踩坑。
读写对称：读取时 model_dump() 会返回普通 dict；再次写回之前需要重新套 Json，不能假设 Prisma 会自动保留包装信息。
日志排查：遇到 Error parsing GraphQL query 或 “unsupported float” 时，优先检查是否存在内联 JSON 对象；通过断点或打印 builder.build() 的结果可快速确认。
兼容性说明：该问题与数据库后端无关（PostgreSQL/MySQL 都会出现），也与 Prisma schema 里 Json 列的类型定义无关。不同版本的 Prisma Python 底层都是 GraphQL，包括 prisma-client-py 2.x/3.x。
实施建议

在服务层引入 Json 包装工具函数，确保所有写入 JSON 列的字段均经过统一转换。
代码评审时关注 “把 dict/list 直塞 Prisma create/update” 的片段，要求封装。
编写回归测试，至少覆盖一个包含连字符键名的 JSON 写入用例，确保不会回退到原始行为。
记录到团队的开发指南中，强调“Prisma Python 内联 JSON 会破坏特殊键名”，并提供封装示例。
如果已有历史数据通过其他语言插入，读取后再保存时仍需包装，避免“读→写”产生新的语法错误。
通过以上规约，可以把这次实际场景总结为“任何写入 Prisma JSON 列的结构都必须显式使用 prisma.Json 包装”的通用准则，从根源上规避 GraphQL 语法错误，保证字段名完整无损地存储到数据库中