Flask中SQLAlchemy多列时间戳插入不一致的解决方法
Flask-SQLAlchemy 中统一插入时间戳的正确处理方式
问题原因
当前模型同时设置了 default=datetime.utcnow 和 server_default=func.now(),其中 datetime.utcnow 是Python本地函数,创建实例时会被分别调用两次(为inserted_at和modified_at赋值),两次调用的时间差导致了毫秒级差异。若仅保留server_default,数据库会生成统一的时间戳,但未提交的模型实例无法获取该值——因为server_default是数据库层面生成的,ORM不会自动同步到未提交的实例中。
解决方案
方案一:重写模型初始化方法,统一赋值时间戳
在模型的__init__方法中生成一个单一的UTC时间,同时赋值给两个时间字段,确保创建实例时就拥有一致的时间值,且提交到数据库时保持一致。
from datetime import datetime from your_app import db class RenterLead(BaseModel): __tablename__ = "renter_leads" uuid = db.Column(db.String, nullable=False, primary_key=True) owner = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) name = db.Column(db.String) email = db.Column(db.String) phone_number = db.Column(db.String) inserted_at = db.Column(db.DateTime, nullable=False) modified_at = db.Column(db.DateTime, nullable=False) def __init__(self, **kwargs): current_time = datetime.utcnow() # 若未传入时间字段,则使用统一生成的时间 kwargs.setdefault('inserted_at', current_time) kwargs.setdefault('modified_at', current_time) super().__init__(**kwargs)
方案二:使用SQLAlchemy事件监听,插入前统一设置时间
通过SQLAlchemy的before_insert事件监听,在记录插入到数据库之前,为两个时间字段赋值同一个UTC时间,既保证数据库中时间一致,也能让实例在插入前拥有时间值。
from datetime import datetime from sqlalchemy import event from your_app import db class RenterLead(BaseModel): __tablename__ = "renter_leads" uuid = db.Column(db.String, nullable=False, primary_key=True) owner = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) name = db.Column(db.String) email = db.Column(db.String) phone_number = db.Column(db.String) inserted_at = db.Column(db.DateTime, nullable=False) modified_at = db.Column(db.DateTime, nullable=False) @event.listens_for(RenterLead, 'before_insert') def set_unified_timestamps(mapper, connection, target): current_time = datetime.utcnow() target.inserted_at = current_time target.modified_at = current_time
补充:仅用数据库生成时间(适合可接受提交后获取值的场景)
如果可以接受未提交实例无时间值,仅保留server_default=func.now(),提交后调用session.refresh()同步数据库生成的时间值,此时两个字段时间完全一致。
from sqlalchemy import func from your_app import db class RenterLead(BaseModel): __tablename__ = "renter_leads" uuid = db.Column(db.String, nullable=False, primary_key=True) owner = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) name = db.Column(db.String) email = db.Column(db.String) phone_number = db.Column(db.String) inserted_at = db.Column(db.DateTime, nullable=False, server_default=func.now()) modified_at = db.Column(db.DateTime, nullable=False, server_default=func.now()) # 使用示例 lead = RenterLead(uuid="unique-uuid", owner=1) db.session.add(lead) db.session.commit() # 同步数据库生成的时间到实例 db.session.refresh(lead)
内容的提问来源于stack exchange,提问作者Daniel Kats
相关产品推荐
相关产品推荐

