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

Pydantic V2:如何实现更智能的created_at、updated_at字段

需求:实现自动维护时间戳的TimestampMixin类

需要实现一个满足以下要求的TimestampMixin类:

  • created_at:创建时自动生成,生成后不可修改
  • updated_at:创建时自动生成
  • updated_at:更新模型字段且未指定该值时,自动更新

示例场景

foo = Foo(name='foo') # created_at、updated_at自动填充

time.sleep(3)

foo.name = 'bar' # `updated_at`自动更新!

data_dict = {"name": "bar", "created_at": 1717288789, "updated_at": 1717288801}
foo_from_orm = Foo.model_validate(**data_dict) # `created_at`: 1717288789, `updated_at`: 1717288801 

当前实现需手动调用on_update方法,无法自动触发更新,代码如下:

from datetime import datetime, UTC

from pydantic import BaseModel, Field


now_factory = lambda: int(datetime.now(UTC).timestamp())

class TimestampMixin(BaseModel):
    created_at: int = Field(default_factory=now_factory)
    updated_at: int = Field(default_factory=now_factory)

    def on_update(self):
        self.updated_at = now_factory()

解决方案

方案1:重写__setattr__实现自动更新与字段保护

利用Python的属性赋值拦截机制,自动更新updated_at并禁止修改created_at,完全适配Pydantic模型的使用逻辑:

from datetime import datetime, UTC
from pydantic import BaseModel, Field

now_factory = lambda: int(datetime.now(UTC).timestamp())

class TimestampMixin(BaseModel):
    created_at: int = Field(default_factory=now_factory)
    updated_at: int = Field(default_factory=now_factory)

    def __setattr__(self, name, value):
        # 禁止修改已生成的created_at
        if name == "created_at" and hasattr(self, "created_at"):
            raise ValueError("created_at字段不可修改")
        
        # 非updated_at字段赋值时,自动更新时间戳
        if name != "updated_at":
            super().__setattr__("updated_at", now_factory())
        
        super().__setattr__(name, value)

# 测试用例
class Foo(TimestampMixin):
    name: str

foo = Foo(name='foo')
print(f"初始状态: created_at={foo.created_at}, updated_at={foo.updated_at}")

import time
time.sleep(1)

foo.name = 'bar'
print(f"更新后: created_at={foo.created_at}, updated_at={foo.updated_at}") # updated_at已自动更新

# 尝试修改created_at会触发报错
try:
    foo.created_at = 123456
except ValueError as e:
    print(e) # 输出: created_at字段不可修改

# 从字典验证模型,保留传入的时间戳
data_dict = {"name": "bar", "created_at": 1717288789, "updated_at": 1717288801}
foo_from_orm = Foo.model_validate(**data_dict)
print(f"从字典创建: created_at={foo_from_orm.created_at}, updated_at={foo_from_orm.updated_at}")

方案2:结合Pydantic验证器增强初始化控制

如果需要在模型验证阶段就对created_at做保护,可以搭配字段验证器使用:

from datetime import datetime, UTC
from pydantic import BaseModel, Field, field_validator

now_factory = lambda: int(datetime.now(UTC).timestamp())

class TimestampMixin(BaseModel):
    created_at: int = Field(default_factory=now_factory)
    updated_at: int = Field(default_factory=now_factory)

    @field_validator('created_at', mode='after')
    def protect_created_at(cls, v, values):
        # 仅允许初始化时设置created_at,后续修改通过__setattr__拦截
        if 'created_at' in values.data:
            raise ValueError("created_at字段不可修改")
        return v

    def __setattr__(self, name, value):
        if name == "created_at" and hasattr(self, "created_at"):
            raise ValueError("created_at字段不可修改")
        
        if name != "updated_at":
            super().__setattr__("updated_at", now_factory())
        
        super().__setattr__(name, value)

方案3:ORM场景适配(以SQLAlchemy为例)

如果和ORM框架配合使用,可以结合ORM的事件监听,在数据库会话提交时自动更新时间戳,兼顾Pydantic模型和数据库操作:

from sqlalchemy import event
from sqlalchemy.orm import Session

# 假设ORM模型继承自TimestampMixin和SQLAlchemy的Base
class FooORM(TimestampMixin):
    __tablename__ = 'foos'
    id = Column(Integer, primary_key=True)
    name = Column(String)

# 监听会话提交前的事件,自动更新脏数据的updated_at
@event.listens_for(Session, 'before_flush')
def update_timestamp(session, flush_context, instances):
    for instance in session.dirty:
        if isinstance(instance, TimestampMixin):
            instance.updated_at = now_factory()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:00:12