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

如何让SQLModel模型中两个字段初始默认时间完全一致?

SQLModel中确保created_at与updated_at首次创建时间一致的问题解答

1. 为什么updated_at: datetime = created_at仍会导致时间不一致?

这种写法只是把类级别上的created_at字段的Field对象赋值给了updated_at,并没有建立实例层面的关联。当创建User实例时,SQLModel会独立处理每个字段的默认值逻辑:

  • created_at通过default_factory=get_current_utc_time生成当前时间
  • updated_at由于没有显式设置默认生成逻辑,SQLModel会根据字段类型自行处理(或视为未设置默认值,后续由其他逻辑生成新时间)
    最终两个字段的时间来自不同的生成调用,自然不一致。

2. 如何通过SQLModel原生功能实现需求?

有两种简洁的原生实现方式:

方式一:重写模型的__init__方法

在实例化时一次性生成时间,同时赋值给两个字段:

from sqlmodel import SQLModel, Field, EmailStr
from datetime import datetime, UTC

def get_current_utc_time():
    return datetime.now(UTC)

class UserBase(SQLModel):
    first_name: str = Field(min_length=2, max_length=50)
    last_name: str = Field(min_length=2, max_length=50)
    email: EmailStr

class User(UserBase, table=True):
    id: int | None = Field(default=None, primary_key=True)
    created_at: datetime = Field(default=None)
    updated_at: datetime = Field(default=None)
    last_login_at: datetime | None = None
    email: EmailStr = Field(unique=True)

    def __init__(self, **data):
        current_time = get_current_utc_time()
        # 优先使用传入的值,未传入则用当前时间
        data.setdefault("created_at", current_time)
        data.setdefault("updated_at", current_time)
        super().__init__(**data)

方式二:使用SQLAlchemy的before_insert事件钩子

利用SQLModel兼容SQLAlchemy事件的特性,在插入数据库前统一设置时间:

from sqlmodel import SQLModel, Field, EmailStr
from datetime import datetime, UTC
from sqlalchemy import event

def get_current_utc_time():
    return datetime.now(UTC)

class UserBase(SQLModel):
    first_name: str = Field(min_length=2, max_length=50)
    last_name: str = Field(min_length=2, max_length=50)
    email: EmailStr

class User(UserBase, table=True):
    id: int | None = Field(default=None, primary_key=True)
    created_at: datetime = Field(default=None)
    updated_at: datetime = Field(default=None)
    last_login_at: datetime | None = None
    email: EmailStr = Field(unique=True)

# 注册插入前事件
@event.listens_for(User, 'before_insert')
def set_created_updated_on_insert(mapper, connection, target):
    current_time = get_current_utc_time()
    target.created_at = current_time
    target.updated_at = current_time

3. 能否用自定义验证器实现?如何确保table=True时生效?

可以通过Pydantic的model_validator实现,配置mode="before"即可确保在实例化阶段(包括ORM模式)触发验证逻辑:

from sqlmodel import SQLModel, Field, EmailStr
from datetime import datetime, UTC
from pydantic import model_validator

def get_current_utc_time():
    return datetime.now(UTC)

class UserBase(SQLModel):
    first_name: str = Field(min_length=2, max_length=50)
    last_name: str = Field(min_length=2, max_length=50)
    email: EmailStr

class User(UserBase, table=True):
    id: int | None = Field(default=None, primary_key=True)
    created_at: datetime | None = None
    updated_at: datetime | None = None
    last_login_at: datetime | None = None
    email: EmailStr = Field(unique=True)

    @model_validator(mode="before")
    def sync_created_updated_time(cls, values):
        # 仅在首次创建且未传入时间时设置统一值
        if values.get("created_at") is None:
            current_time = get_current_utc_time()
            values["created_at"] = current_time
            values["updated_at"] = current_time
        return values

关键说明:

  • 使用model_validator而非旧版@validator,对ORM模式支持更完善
  • mode="before"会在模型实例化前处理数据,无论通过Pydantic的User(**data)还是ORM的session.add(User(...))创建实例,都会触发该验证器
  • 逻辑中判断created_at是否为None,确保仅在首次创建时同步时间,后续更新时可单独修改updated_at

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 21:07:24