SQLAlchemy: python类的属性值为None,数据为JSON类型,插入数据库为‘ NULL‘字符串,而不是真正的NULL

描述:

最近使用python orm框架SQLAlchemy时,遇到mysql数据库表字段类型为json类型,python实体类属性对应值为None,但是插入数据库后为‘ NULL‘字符串,而不是真正的NULL

实体类:

from sqlalchemy import Column, DateTime, Float, Integer, JSON, String, text
from sqlalchemy.dialects.mysql import VARCHAR
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
metadata = Base.metadata


class User(Base):
    __tablename__ = 'user'
    __table_args__ = {'comment': '用户表'}

    id = Column(String(50, 'utf8mb4_general_ci'), primary_key=True)
    user_name = Column(VARCHAR(50), comment='用户名')
    user_info = Column(JSON, comment='用户信息')

 解决办法:

这里的user_info在mysql数据库是json类型,在python中将user_info赋值为: null(),这样该字段为空时,数据库的json类型字段值就为真正的NULL

from sqlalchemy import null

# 用户信息
user_info = null()

完整代码

from sqlalchemy import null

# 生成guid
id = str(uuid.uuid4()).replace('-', '').upper()
# 用户名
name = "张三"
# 用户信息
user_info = null()
# 实体类赋值
user = User(
    id=id,
    name=name,
    user_info=user_info
)

参考资料:

https://cloud.tencent.com/developer/ask/sof/106856558

<think>好的,我现在需要根据用户提供的数据库表结构,设计Flask和SQLAlchemy的模型文件以及接口视图文件。用户还提到了要使用统一的返回格式,所以得确保每个接口都返回符合要求的JSON响应。 首先,先看数据库表的结构。表名是position_source,包含的字段有id(主键)、name、type、url、create_time。每个字段的类型和注释都列出来了。我需要创建对应的SQLAlchemy模型。 模型应该继承自db.Model,表名可能需要明确设置,因为默认情况下SQLAlchemy会使用名的小写形式,但这里表名是position_source,所以需要指定__tablename__属性。每个字段的类型和参数需要对应数据库中的定义。例如,id是整数主键,其他字段是字符串,允许为空,create_time是DateTime类型,默认可能需要设置为当前时间,或者在插入时自动生成。 接下来是接口设计。通常,RESTful API需要基本的CRUD操作:创建、读取、更新、删除。所以可能需要创建以下接口: 1. 获取所有岗位来源的列表(GET /api/position_sources) 2. 获取单个岗位来源的详情(GET /api/position_sources/<id>) 3. 创建新的岗位来源(POST /api/position_sources) 4. 更新现有岗位来源(PUT /api/position_sources/<id>) 5. 删除岗位来源(DELETE /api/position_sources/<id>) 每个接口都需要处理请求参数,执行相应的数据库操作,并返回统一格式的响应。例如,创建和更新操作需要解析JSON请求体,验证数据,然后保存到数据库。删除操作需要检查是否存在对应的记录,再进行删除。 在视图函数中,需要使用SQLAlchemy的会话进行数据库操作,并处理可能的异常,比如找不到记录或数据库错误。当出现错误时,返回相应的错误代码和消息,如code=404时message为“未找到”。 关于时间字段create_time,可能需要设置默认为datetime.utcnow,这样在创建新记录时自动填充当前时间。 还要注意Flask的路由和蓝图的配置,可能需要将接口组织在一个蓝图中,方便管理。例如,创建一个名为position_source_bp的蓝图,将所有相关路由注册到该蓝图下。 另外,用户的返回格式函数api_response已经给出,需要在每个视图函数中调用这个函数来构造响应,确保返回的JSON结构一致。例如,成功时返回code=200和data,失败时返回适当的错误码和message。 可能还需要处理请求参数的验证,比如在创建和更新时,检查必要字段是否存在,或者字段类型是否正确。如果缺少必要字段,返回400错误。 最后,需要确保所有导入的模块正确,比如从flask导入Blueprint、request、jsonify,以及SQLAlchemy的相关模块。同时,模型需要与数据库连接,因此需要确保Flask应用已经配置了SQLALCHEMY_DATABASE_URI,并初始化了db对象。 可能的问题点: 1. 字段允许为空的情况,例如name、type、url都是DEFAULT NULL,所以在模型中应该设置nullable=True。 2. create_time的默认是否正确设置,是否需要使用服务器时间。 3. 主键id是否为自增?在模型中可能需要设置autoincrement=True,但根据用户提供的建表语句,id是主键但没有自增属性,所以可能需要手动分配id,或者在模型中设置自增。这里可能需要确认,但通常主键如果是int类型且自增的话,应该在模型中将id设置为自动递增。但根据建表语句,没有AUTO_INCREMENT,所以可能用户需要自己处理id的生成,这时候在创建记录时必须提供id的,否则会出错。这可能是个问题,需要用户确认,但根据当前信息,模型中可能不需要设置自增,而是由应用层处理id的分配。 例如,用户提供的建表语句中id是int NOT NULL,主键,但没有自增属性,所以在创建记录时,必须显式提供id的,否则会引发错误。这可能需要在接口中要求客户端发送id,或者在服务端生成id。这里可能需要进一步确认,但根据现有信息,模型中的id字段不应设置自增,而是由客户端提供。但这样可能导致id冲突,所以通常建议使用自增主键。不过根据用户提供的SQL,可能他们有自己的id生成方式,所以模型中的id字段应设置为primary_key=True,但不需要自增。 总结步骤: 1. 创建模型PositionSource,字段对应数据库表,注意nullable设置和默认。 2. 创建蓝图,定义路由。 3. 实现各个视图函数,处理请求,调用api_response返回统一格式。 4. 处理异常情况,比如数据库操作失败或记录不存在。 5. 确保请求参数的正确解析和验证。</think>我将分步骤为您设计基于Flask和SQLAlchemy的模型及接口实现: ```python # ---------------------- # 模型文件 models.py # ---------------------- from datetime import datetime from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() class PositionSource(db.Model): __tablename__ = 'position_source' id = db.Column(db.Integer, primary_key=True, comment='ID') name = db.Column(db.String(255), nullable=True, comment='名称') type = db.Column(db.String(255), nullable=True, comment='类型') url = db.Column(db.String(255), nullable=True, comment='来源链接') create_time = db.Column(db.DateTime, default=datetime.utcnow, comment='创建时间') def to_dict(self): return { 'id': self.id, 'name': self.name, 'type': self.type, 'url': self.url, 'create_time': self.create_time.isoformat() if self.create_time else None } ``` ```python # ---------------------- # 视图文件 views.py # ---------------------- from flask import Blueprint, request from models import db, PositionSource from datetime import datetime position_bp = Blueprint('position', __name__, url_prefix='/api/position_sources') @position_bp.route('', methods=['GET']) def get_all_positions(): try: positions = PositionSource.query.all() return api_response(data=[p.to_dict() for p in positions]) except Exception as e: return api_response(code=500, message=str(e)) @position_bp.route('/<int:position_id>', methods=['GET']) def get_position(position_id): position = PositionSource.query.get(position_id) if not position: return api_response(code=404, message="记录不存在") return api_response(data=position.to_dict()) @position_bp.route('', methods=['POST']) def create_position(): try: data = request.get_json() # 手动ID处理(根据表结构设计) new_position = PositionSource( id=data['id'], name=data.get('name'), type=data.get('type'), url=data.get('url'), create_time=datetime.utcnow() ) db.session.add(new_position) db.session.commit() return api_response(data=new_position.to_dict(), code=201) except KeyError: return api_response(code=400, message="缺少必要字段id") except Exception as e: db.session.rollback() return api_response(code=500, message=str(e)) @position_bp.route('/<int:position_id>', methods=['PUT']) def update_position(position_id): try: position = PositionSource.query.get(position_id) if not position: return api_response(code=404, message="记录不存在") data = request.get_json() position.name = data.get('name', position.name) position.type = data.get('type', position.type) position.url = data.get('url', position.url) db.session.commit() return api_response(data=position.to_dict()) except Exception as e: db.session.rollback() return api_response(code=500, message=str(e)) @position_bp.route('/<int:position_id>', methods=['DELETE']) def delete_position(position_id): try: position = PositionSource.query.get(position_id) if not position: return api_response(code=404, message="记录不存在") db.session.delete(position) db.session.commit() return api_response(message="删除成功") except Exception as e: db.session.rollback() return api_response(code=500, message=str(e)) ``` ```python # ---------------------- # 工具函数(建议放在utils/response.py) # ---------------------- from flask import jsonify def api_response(data=None, code=200, message='success'): return jsonify({ 'code': code, 'message': message, 'data': data }), code ``` 关键设计要点说明: 1. **模型设计**: - 使用`db.Column`精确匹配表结构 - `create_time`字段使用UTC时间自动生成 - 添加`to_dict()`方法实现模型序列化 2. **接口规范**: - 符合RESTful设计风格 - 支持标准HTTP方法:GET/POST/PUT/DELETE - 统一错误处理(404/500等) 3. **安全增强建议**: ```python # 可在视图函数中添加参数校验(示例) def validate_position_data(data): if len(data.get('name', '')) > 255: raise ValueError("名称过长") # 添加其他校验规则 ``` 4. **部署注意事项**: ```python # 需要在app初始化时配置数据库 from flask import Flask app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://user:password@localhost/dbname' db.init_app(app) ``` 需要特别注意: 1. 根据表结构设计,`id`字段需要客户端显式传递(非自增) 2. 所有时间字段均使用UTC标准时间 3. 建议在生产环境添加: - 身份验证(JWT等) - 请求速率限制 - 更完善的参数校验 - 数据库连接池配置
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值