如何在Pydantic中对GeoJSON对象的坐标进行6位小数取整?
GeoJSON Polygon坐标保留6位小数的Pydantic实现问题
需求
使用Python 3.10.6,需将GeoJSON的Polygon坐标(CustomCoordinates中的纬度lat和经度lon)保留6位小数,要求不通过循环遍历,而是在基础层级处理。已导入pydantic_geojson的内置几何类,需修改坐标实现6位小数取整。
尝试过的方案及问题
方案1:给LonField添加decimal_places=6约束
尝试在Field中设置decimal_places=6:
LonField = Annotated[ Union[float, float], Field( title='Coordinate longitude', decimal_places=6, gt=-180, lt=180, ) ]
触发报错:
File "pydantic\main.py", line 198, in pydantic.main.ModelMetaclass.__new__ File "pydantic\fields.py", line 506, in pydantic.fields.ModelField.infer File "pydantic\fields.py", line 436, in pydantic.fields.ModelField.__init__ File "pydantic\fields.py", line 552, in pydantic.fields.ModelField.prepare File "pydantic\fields.py", line 661, in pydantic.fields.ModelField._type_analysis File "pydantic\fields.py", line 758, in pydantic.fields.ModelField._type_analysis File "pydantic\fields.py", line 808, in pydantic.fields.ModelField._create_sub_type File "pydantic\fields.py", line 436, in pydantic.fields.ModelField.__init__ File "pydantic\fields.py", line 552, in pydantic.fields.ModelField.prepare File "pydantic\fields.py", line 758, in pydantic.fields.ModelField._type_analysis File "pydantic\fields.py", line 808, in pydantic.fields.ModelField._create_sub_type File "pydantic\fields.py", line 436, in pydantic.fields.ModelField.__init__ File "pydantic\fields.py", line 557, in pydantic.fields.ModelField.prepare File "pydantic\fields.py", line 831, in pydantic.fields.ModelField.populate_validators File "pydantic\validators.py", line 735, in find_validators File "pydantic\validators.py", line 599, in pydantic.validators.make_namedtuple_validator File "pydantic\annotated_types.py", line 72, in pydantic.annotated_types.create_model_from_namedtuple File "pydantic\main.py", line 1026, in pydantic.main.create_model File "pydantic\main.py", line 198, in pydantic.main.ModelMetaclass.__new__ File "pydantic\fields.py", line 504, in pydantic.fields.ModelField.infer File "pydantic\schema.py", line 1011, in pydantic.schema.get_annotation_from_field_info ValueError: On field "lon" the following field constraints are set but not enforced: decimal_places.
方案2:使用验证器但未生效
尝试给CustomCoordinates添加验证器,但未实现取整效果:
class CustomCoordinates(NamedTuple): lat: LatField lon: LonField @validator('lat') def coordinates_roundOff(cls,v): return round(v, 6)
当前相关代码
LonField = Annotated[ Union[float, float], Field( title='Coordinate longitude', gt=-180, lt=180, ) ] LatField = Annotated[ Union[float, float], Field( title='Coordinate latitude', gt=-90, lt=90, ), ] class CustomCoordinates(NamedTuple): lat: LatField lon: LonField class PolygonGeo(CamelModel): type: Optional[str] = Field(None, description='Type of GeoJSon geometry', example='Polygon') coordinates: Optional[List[List['CustomCoordinates']]] = Field( None, description='Coordinates of GeoJSon geometry', example='[[[-73.7786925003853,40.63992777725],...]]]')
解决方案
核心原因
decimal_places约束仅对Decimal类型生效,无法作用于float类型,直接添加会触发未生效约束报错。- 标准库
NamedTuple不支持Pydantic的验证器逻辑,导致添加的验证器无法触发。
实现方法
方法1:自定义带取整逻辑的字段类型
将取整逻辑封装到自定义字段类型中,所有使用该类型的字段会自动完成6位小数取整:
from pydantic import GetCoreSchemaHandler, Field, Annotated from pydantic_core import core_schema from pydantic import CamelModel, BaseModel from typing import Optional, List # 自定义带6位小数取整的float类型 class RoundedFloat(float): @classmethod def __get_pydantic_core_schema__(cls, source_type, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: return core_schema.no_info_wrap_validator_function( cls.validate, handler(float), ) @classmethod def validate(cls, v: float) -> float: return round(v, 6) # 定义经纬度字段,继承自定义类型并添加范围约束 LonField = Annotated[ RoundedFloat, Field( title='Coordinate longitude', gt=-180, lt=180, ) ] LatField = Annotated[ RoundedFloat, Field( title='Coordinate latitude', gt=-90, lt=90, ), ] # 改用Pydantic BaseModel替代NamedTuple,确保验证逻辑生效 class CustomCoordinates(BaseModel): lat: LatField lon: LonField class PolygonGeo(CamelModel): type: Optional[str] = Field(None, description='GeoJSON几何类型', example='Polygon') coordinates: Optional[List[List[CustomCoordinates]]] = Field( None, description='GeoJSON几何坐标', example='[[[-73.7786925003853,40.63992777725],...]]]')
方法2:给BaseModel添加字段验证器
如果偏好使用验证器的方式,可改用BaseModel并添加字段级验证器:
from pydantic import field_validator class CustomCoordinates(BaseModel): lat: LatField lon: LonField @field_validator('lat', 'lon') def round_coordinates(cls, v: float) -> float: return round(v, 6) # PolygonGeo定义保持不变
说明
- 改用
BaseModel替代NamedTuple是因为Pydantic对BaseModel的验证逻辑支持更完善,能确保在字段解析的基础层级自动处理坐标取整,无需手动遍历整个Polygon坐标数组。 - 自定义
RoundedFloat类型的方式更符合“基础层级处理”的需求,将取整逻辑与字段类型绑定,复用性更强。
内容的提问来源于stack exchange,提问作者Darshan Patil
相关产品推荐
相关产品推荐

