fastapi将字符串数组和字典序列化为JSON字符串存储到数据库

3 篇文章 0 订阅
1 篇文章 0 订阅

近期的fastapi项目中,在设计数据结构时发现有将字符串数组和字典对象固化到数据库的需求。fastapi的数据结构以Pydantic为核心,所以解决方案必然要去Pydantic序列化函数文档找。下面直接给出示例代码:

#!/usr/bin/python3
# coding: utf-8
# model.py

from datetime import datetime
from sqlalchemy import Column, String
from sqlalchemy.ext.declarative import declarative_base, declared_attr
import re

class CustomBase:
    """https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/mixins.html"""
    # @declared_attr
    # def __tablename__(cls):
    #     return cls.__name__.lower()
    # 驼峰命名转为小写下划线命名: https://blog.csdn.net/mouday/article/details/90079956
    @declared_attr.directive
    def __tablename__(cls) -> str:
        snake_case = re.sub(r"(?P<key>[A-Z])", r"_\g<key>", cls.__name__)
        return snake_case.lower().strip('_')
    __table_args__ = {
        'mysql_engine': 'InnoDB',
        'mysql_collate': 'utf8mb4_general_ci'
    }

BaseModel = declarative_base(cls=CustomBase)

class CommonMixin:
    is_deleted: Mapped[Optional[int]] = \
        mapped_column(SmallInteger, server_default=text('0'), comment="是否删除: 0 未删除 1 已删除")
    create_time: Mapped[Optional[datetime]] = \
        mapped_column(insert_default=func.now(), comment="创建时间")
    update_time: Mapped[Optional[datetime]] = \
        mapped_column(server_default=func.now(), onupdate=func.now(), comment="更新时间")

class Waypoint(BaseModel, CommonMixin):
    id: Mapped[str] = mapped_column(String(256), primary_key=True, comment="主键ID")
    map_id: Mapped[str] = mapped_column(String(256) , comment="点位所属地图")
    key: Mapped[str] = mapped_column(String(256),unique=True, comment="点位标识")
    name: Mapped[str] = mapped_column(String(256),unique=True, comment="点位名称")
    point_type: Mapped[Optional[int]] = \
        mapped_column(Integer, server_default=text('4'), comment="点位类型 0:普通点 1:休息点 2:充电点 3:数据采集点点 4:任务终点")
    adjacent_points: Mapped[Optional[str]] = mapped_column(String(1024), comment="拓扑地图邻接点位,用Json String保存相应List")
    robot_payload: Mapped[Optional[str]] = mapped_column(String(1024), comment="机器人点位运行负载,用Json String保存动作参数")

上述model.py代码除了构建常规的sqlachemy BaseModel,主要还定义了Waypoint这个ORM数据结构,adjacent_pointsrobot_payload这两个字段将在schema.py里映射为List[str]dict. 然后是schema.py代码:

#!/usr/bin/python3
# coding: utf-8
# schema.py

from pydantic import BaseModel, Tag, Field, ConfigDict, field_serializer, field_validator
from typing import List, Optional
from typing_extensions import Annotated
from datetime import datetime
from pydantic.alias_generators import to_snake
import json

class InDBMixin(BaseModel):
    class Config:
        orm_mode = True  # 通过Pydantic将ORM对象转化为Pydantic对象

class BaseWaypoint(BaseModel):
    id: str = Field(description='点位ID')
    mapId: str = Field(description='点位地图ID')
    key: str = Field(description='点位标识')
    name: str = Field(description='点位名称')
    pointType: int = Field(description='点位类型 0:普通点 1:休息点 2:充电点 3:数据采集点点 4:任务终点')
    adjacentPoints: Optional[List[str]] = Field(description='拓扑地图邻接点位')
    robotPayload: Optional[dict] = Field(description='机器人点位运行负载,用Json String保存动作参数',default=None)

    @field_serializer('adjacentPoints', when_used='always')
    def serialize_adjacent_points_to_json(adjacentPoints: List[str]):
        return json.dumps(adjacentPoints)
    
    @field_validator('adjacentPoints', mode="before")
    def parse_adjacent_points(cls, value):
        if isinstance(value,str):
            return json.loads(value)
        return value
    
    @field_serializer('robotPayload', when_used='always')
    def serialize_robot_payload_to_json(adjacentPoints: List[str]):
        return json.dumps(adjacentPoints)
    
    @field_validator('robotPayload', mode="before")
    def parse_robot_payload(cls, value):
        if isinstance(value,str):
            return json.loads(value)
        return value

    model_config = ConfigDict(
        alias_generator=to_snake,
        populate_by_name=True
    )

class WaypointSchema(BaseWaypoint, InDBMixin):
    createTime: datetime
    updateTime: datetime

class CreateWaypointSchema(BaseWaypoint):
    pass

class UpdateWaypointSchema(BaseWaypoint):
    pass

可以看到上述schema.py的代码adjacentPointsrobotPayload已经不再是字符串。细心的读者会发现代码风格变了,是因为通过Pydantic的alias_generatorpopulate_by_name配置,自动生成了snake_casealias,这在跨语言跨风格的接口对接下比较有用。

上述代码中,通过field_serializer注解实现了序列化为JSON字符串的需求。需要注意点是,配置了该注解后在任何类似model_dump的场景,模型导出的都将是JSON字符串。反方向地,在从数据库中的JSON字符串构建WaypointSchema的过程中,字符串将被赋值给WaypointSchema的构造函数。通过field_validator注解最终实现了将JSON字符串转换为对应数据结构的过程。这个注解值得注意的是:与序列化只能输出字符串不同,对象的构造过程可以接收不同类型的数据,比如通过API去构造时就可以直接接收对象而不用先转成字符串。

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会尽量帮助你解决这个问题。首先,我需要确认一下你的问题,是指读取CSV文件中超过单元格长度限制的字符串,然后将其序列化JSON字符串,是吗?如果是这样的话,我可以给你提供一个大致的思路: 1. 读取CSV文件,可以使用Python内置的csv模块进行操作; 2. 对于超过单元格长度限制的字符串,可以使用Python字符串处理方法进行处理,将其拼接成完整的字符串; 3. 将每一行处理后的数据存储到一个字典中,并将字典添加到一个列表中,最后将列表序列化JSON字符串。 下面是Python的代码示例: ```python import csv import json def read_csv_file(file_path): """ 读取CSV文件,并将超过单元格长度限制的字符串拼接成完整的字符串 """ data_list = [] with open(file_path, newline='', encoding='utf-8-sig') as csvfile: reader = csv.reader(csvfile) for row in reader: new_row = [] for item in row: if len(item) > 32767: # 假设单元格长度限制为32767 # 将超过长度限制的字符串拼接成完整的字符串 full_str = '' for i in range(0, len(item), 32767): full_str += item[i:i+32767] new_row.append(full_str) else: new_row.append(item) data_list.append(new_row) return data_list def serialize_to_json(data_list): """ 将CSV文件读取后的数据序列化JSON字符串 """ result_list = [] header = data_list[0] for row in data_list[1:]: row_dict = {} for i in range(len(header)): row_dict[header[i]] = row[i] result_list.append(row_dict) return json.dumps(result_list) if __name__ == '__main__': file_path = 'test.csv' data_list = read_csv_file(file_path) json_str = serialize_to_json(data_list) print(json_str) ``` 以上代码中,`read_csv_file()`函数实现了读取CSV文件的功能,并将超过单元格长度限制的字符串拼接成完整的字符串;`serialize_to_json()`函数将CSV文件读取后的数据序列化JSON字符串。你可以将以上代码复制到Python文件中运行,看看是否满足你的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值