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

