如何让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
相关产品推荐
相关产品推荐

