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

FastAPI+Firestore中Pydantic模型兼容Firestore服务器时间戳方案

解决Firestore SERVER_TIMESTAMP与Pydantic模型的类型冲突问题

方案1:扩展字段类型并添加自定义验证

将createdOn字段的类型改为支持datetime和Firestore的Sentinel类型,同时添加验证器允许Sentinel直接通过:

from datetime import datetime
from typing import Optional, Union
from pydantic import BaseModel, Field, validator
from google.cloud.firestore_v1 import Sentinel

class NGO(BaseModel): 
    displayName: str = Field(..., example="Lorem Ipsum")
    tagline: str = Field(..., example="Lorem Ipsum")
    description: str = Field(..., example="Lorem Ipsum")
    status: str = Field(..., example="active")
    logoUrl: str = Field(..., example="https://www.thehindu.com/static/theme/default/base/img/branding/logo.png")
    bannerUrl: list = Field(..., example=["https://www.thehindu.com/static/theme/default/base/img/branding/logo.png"])
    contactName: str = Field(..., example="Lorem Ipsum")
    contactEmail: str = Field(..., example="abcd@gmail.com")
    contactNumber: str = Field(..., example="+91 1234567890")
    website: str = Field(..., example="https://www.thehindu.com")
    associatedMagazines: Optional[list] = Field(None, example=["abc123"])
    associatedProjects: Optional[list] = Field(None, example=["abc123"])
    createdBy: Optional[str] = Field(None, example="Lorem Ipsum")
    updatedBy: Optional[str] = Field(None, example="Lorem Ipsum")
    isFeatured: Optional[bool] = Field(None, example=False)
    publicRegistryId: Optional[str] = Field(None, example="abc123")
    createdOn: Optional[Union[datetime, Sentinel]] = Field(None, example=datetime.now())

    @validator("createdOn", pre=True)
    def validate_created_on(cls, v):
        if isinstance(v, Sentinel):
            return v
        return v

修改后即可直接为createdOn赋值firestore.SERVER_TIMESTAMP,同时保留原有的datetime类型支持。

方案2:绕过Pydantic验证,在字典中添加时间戳

如果不想修改模型结构,可以先创建不含createdOn的NGO对象,转为字典后再追加Firestore时间戳:

# 创建不含createdOn的NGO实例
ngo = NGO(
    displayName=displayName,
    description=description,
    tagline=tagline,
    status=status,
    logoUrl=logoUrl,
    bannerUrl=bannerUrl,
    contactName=contactName,
    contactEmail=contactEmail,
    contactNumber=contactNumber,
    website=website,
    updatedBy=updatedBy,
    isFeatured=isFeatured,
    publicRegistryId=publicRegistryId,
)
# 转为字典后添加SERVER_TIMESTAMP
ngo_dict = ngo.dict()
ngo_dict["createdOn"] = firestore.SERVER_TIMESTAMP

# 将ngo_dict写入Firestore即可

这种方式无需改动模型,适合快速实现。

方案3:自定义Pydantic字段类型(长期优雅方案)

针对Firestore时间戳创建自定义字段类型,封装类型兼容逻辑:

from datetime import datetime
from typing import Any
from pydantic import BaseModel, Field
from pydantic_core import core_schema
from google.cloud.firestore_v1 import Sentinel

class FirestoreTimestamp:
    @classmethod
    def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> core_schema.CoreSchema:
        # 支持datetime、Sentinel和None类型
        return core_schema.union_schema([
            core_schema.datetime_schema(),
            core_schema.is_instance_schema(Sentinel),
            core_schema.none_schema()
        ])

class NGO(BaseModel): 
    # 其他字段保持不变...
    createdOn: Optional[FirestoreTimestamp] = Field(None, example=datetime.now())

自定义字段类型让模型结构更清晰,适合大型项目复用。

替代Pydantic的方案:直接使用字典操作

如果不需要Pydantic的验证和文档生成能力,可以直接用字典构造数据:

ngo_dict = {
    "displayName": displayName,
    "description": description,
    "tagline": tagline,
    "status": status,
    "logoUrl": logoUrl,
    "bannerUrl": bannerUrl,
    "contactName": contactName,
    "contactEmail": contactEmail,
    "contactNumber": contactNumber,
    "website": website,
    "updatedBy": updatedBy,
    "isFeatured": isFeatured,
    "publicRegistryId": publicRegistryId,
    "createdOn": firestore.SERVER_TIMESTAMP,
}
# 直接将字典写入Firestore

这种方式最灵活,但需要自行保证数据合法性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:53:16