Pydantic经纬度验证遇model_type错误及单元测试问题
问题分析与解决
1. 初始 model_type 错误原因及修复
你最初的代码同时混用了Pydantic的BaseModel继承和@dataclass装饰器,这是错误用法——Pydantic的dataclass(来自pydantic.dataclasses)和BaseModel是两种独立的定义数据结构的方式,不能同时使用。
错误代码问题点:
from pydantic import BaseModel, Field from pydantic.dataclasses import dataclass @dataclass(frozen=True) class Location(BaseModel): # 错误:同时使用dataclass装饰器和继承BaseModel longitude: float = Field(None, ge=-180, le=180) latitude: float = Field(None, ge=-90, le=90)
修复方案二选一:
方案A:使用BaseModel(适合API场景)
from pydantic import BaseModel, Field class Location(BaseModel): longitude: float | None = Field(None, ge=-180, le=180) latitude: float | None = Field(None, ge=-90, le=90) # 实例化正常 Location(longitude=1.0, latitude=1.0)
方案B:使用Pydantic Dataclass(适合传统数据类场景)
from pydantic import Field from pydantic.dataclasses import dataclass @dataclass(frozen=True) class Location: longitude: float | None = Field(None, ge=-180, le=180) latitude: float | None = Field(None, ge=-90, le=90) # 实例化正常 Location(longitude=1.0, latitude=1.0)
2. 单元测试的问题及修复
移除BaseModel后,测试问题有两个核心原因:
- Pydantic Dataclass实例化时立即执行验证,所以你第一行创建
invalid_location的代码就已经抛出异常,根本不会进入后续的with断言块。 - Pydantic默认抛出的是
ValidationError(不是ValueError),且错误信息不是你预期的"Location is out of range"。
测试代码的问题点:
def test_when_location_is_not_in_range_then_print_exception(self): # 此处已抛出ValidationError,后续代码不会执行 invalid_location = Location(latitude=200.0, longitude=300.0) with self.assertRaises(ValueError) as context: location = Location(latitude=invalid_location.latitude, longitude=invalid_location.longitude) self.assertEqual( str(context.exception), "Location is out of range", "Expected exception message: 'Location is out of range'" )
修复后的测试代码:
方案1:捕获ValidationError并断言默认错误内容
from pydantic import ValidationError import unittest class TestLocation(unittest.TestCase): def test_invalid_location_raises_validation_error(self): with self.assertRaises(ValidationError) as context: Location(latitude=200.0, longitude=300.0) # 断言错误信息包含预期的验证提示 self.assertIn("Input should be less than or equal to 180", str(context.exception)) self.assertIn("Input should be less than or equal to 90", str(context.exception))
方案2:自定义验证器,抛出指定错误信息
如果需要自定义错误提示,可添加字段验证器:
from pydantic import Field, validator from pydantic.dataclasses import dataclass @dataclass(frozen=True) class Location: longitude: float | None = Field(None, ge=-180, le=180) latitude: float | None = Field(None, ge=-90, le=90) @validator('longitude') def validate_longitude(cls, v): if v is not None and not (-180 <= v <= 180): raise ValueError("Location is out of range") return v @validator('latitude') def validate_latitude(cls, v): if v is not None and not (-90 <= v <= 90): raise ValueError("Location is out of range") return v
对应的测试代码:
def test_invalid_location_raises_custom_error(self): with self.assertRaises(ValueError) as context: Location(latitude=200.0, longitude=300.0) self.assertEqual(str(context.exception), "Location is out of range")
关于Unexpected Argument警告
该警告多为IDE误报,因为Pydantic Dataclass的字段是动态生成的。可通过安装Pydantic专属IDE插件(如PyCharm的Pydantic插件)解决,或直接忽略该警告。
内容的提问来源于stack exchange,提问作者PepeLoperena
相关产品推荐
相关产品推荐

