You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python+Flask REST API如何实现数据键在驼峰式与蛇形命名间转换?

嘿,这个需求我太熟了!刚好结合你在用的Flask+SQLAlchemy+Marshmallow技术栈,有个非常贴合的最优方案,还有备用方案给你参考:

最优方案:基于Marshmallow Schema的自动转换

既然你已经在用Marshmallow做序列化/反序列化,那直接在Schema层面实现键名转换是最精准、最贴合现有技术栈的方案——完全不需要全局修改请求响应逻辑,只在数据序列化的环节处理,不会干扰其他业务。

步骤1:定义基础Schema类

先写一个通用的BaseSchema,内置camelCase转snake_case(接收前端数据时)和snake_case转camelCase(返回响应时)的递归转换逻辑:

from marshmallow import Schema, pre_load, post_dump

class BaseSchema(Schema):
    def _camel_to_snake(self, data):
        # 递归处理字典、列表和嵌套结构
        if isinstance(data, dict):
            converted = {}
            for key, value in data.items():
                # 把camelCase转成snake_case:比如userName → user_name
                snake_key = ''.join(['_' + c.lower() if c.isupper() else c for c in key]).lstrip('_')
                converted[snake_key] = self._camel_to_snake(value)
            return converted
        elif isinstance(data, list):
            return [self._camel_to_snake(item) for item in data]
        else:
            return data

    def _snake_to_camel(self, data):
        # 递归处理字典、列表和嵌套结构
        if isinstance(data, dict):
            converted = {}
            for key, value in data.items():
                # 把snake_case转成camelCase:比如user_name → userName
                camel_key = ''.join([word.capitalize() if i > 0 else word for i, word in enumerate(key.split('_'))])
                converted[camel_key] = self._snake_to_camel(value)
            return converted
        elif isinstance(data, list):
            return [self._snake_to_camel(item) for item in data]
        else:
            return data

    @pre_load
    def handle_incoming_data(self, data, **kwargs):
        # 前端发来的camelCase数据,预处理转成snake_case
        return self._camel_to_snake(data)

    @post_dump
    def handle_outgoing_data(self, data, **kwargs):
        # 返回给前端时,把snake_case转成camelCase
        return self._snake_to_camel(data)

步骤2:让业务Schema继承BaseSchema

你的SQLAlchemy模型对应的Schema只需要继承这个BaseSchema,再结合SQLAlchemyAutoSchema就能自动完成转换:

from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
from your_app import db
from models import User  # 假设你的SQLAlchemy模型是User,字段都是snake_case

class UserSchema(BaseSchema, SQLAlchemyAutoSchema):
    class Meta:
        model = User
        load_instance = True  # 允许把转换后的snake_case数据直接转成模型实例
        sqla_session = db.session

步骤3:在API路由中使用Schema

这样在接收前端Angular传来的JSON时,Schema会自动把userName转成user_name,匹配你的SQLAlchemy模型字段;返回响应时,又会把user_name转回userName给前端:

from flask import request, jsonify
from schemas import UserSchema

user_schema = UserSchema()
users_schema = UserSchema(many=True)

@app.route('/api/users', methods=['POST'])
def create_user():
    # 前端发来的是camelCase的JSON,比如{"userName": "Alice", "emailAddress": "alice@example.com"}
    user_data = user_schema.load(request.get_json())
    # user_data已经是转成snake_case的User实例:user_data.user_name, user_data.email_address
    db.session.add(user_data)
    db.session.commit()
    # 返回时自动转成camelCase
    return jsonify(user_schema.dump(user_data)), 201
备用方案:全局请求响应钩子

如果你需要对所有API路由做统一转换,也可以用Flask的全局钩子,但要注意排除非API路由(比如静态文件):

from flask import request, jsonify

def camel_to_snake(data):
    if isinstance(data, dict):
        return {''.join(['_' + c.lower() if c.isupper() else c for c in key]).lstrip('_'): camel_to_snake(value) for key, value in data.items()}
    elif isinstance(data, list):
        return [camel_to_snake(item) for item in data]
    else:
        return data

def snake_to_camel(data):
    if isinstance(data, dict):
        return {''.join([word.capitalize() if i > 0 else word for i, word in enumerate(key.split('_'))]): snake_to_camel(value) for key, value in data.items()}
    elif isinstance(data, list):
        return [snake_to_camel(item) for item in data]
    else:
        return data

@app.before_request
def preprocess_incoming_data():
    # 只处理API前缀的路由
    if request.path.startswith('/api/') and request.is_json:
        request.json = camel_to_snake(request.get_json())

@app.after_request
def postprocess_outgoing_data(response):
    # 只处理API前缀的路由
    if request.path.startswith('/api/') and response.is_json:
        response_data = response.get_json()
        response.set_data(json.dumps(snake_to_camel(response_data)))
    return response
方案对比
  • Marshmallow Schema方案:更推荐!精准控制转换范围,和现有序列化逻辑深度整合,不会影响非API业务,嵌套结构处理更可靠。
  • 全局钩子方案:适合需要全API统一转换的场景,但要注意排除不需要转换的路由,避免意外问题。

内容的提问来源于stack exchange,提问作者Yoh0xFF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:14:10