在 pydantic 设置管理中使用 Class 作为 Filed 属性
Use Class as a Filed attribute in pydantic settings management
我正在使用 Pydantic 进行设置管理,现在我遇到了一个问题。
假设我对多个环境有不同的设置 class。现在我想使用 class 作为不同设置环境的字段属性,并在测试期间更改它们。问题是我能够获取 class,但没有在执行期间明确设置它。这是一个简短的例子:
In [96]: from typing import TypeVar
In [97]: UserSchemaType = TypeVar("UserSchemaType", bound=BaseModel)
In [98]: from pydantic import BaseModel
In [99]: class User(BaseModel):
...: id: str
...:
In [100]: class CustomUser(BaseModel):
...: id: str
...: first_name: str
...: last_name: str
...:
In [101]: class AppSettings(BaseSettings):
...: # some settings
...: foo: str = 'foo'
...:
...: user_class: UserSchemaType = User
...:
In [102]: class TestAppSettings(AppSettings):
...: # some settings
...: pass
...:
In [103]: test_app_settings = TestAppSettings()
In [104]: test_app_settings.dict()
Out[104]: {'foo': 'foo'}
In [105]: test_app_settings.user_class
Out[105]: __main__.User
In [106]: test_app_settings.user_class = CustomUser
ValueError: "TestAppSettings" object has no field "user_class"
AppSettings.user_class
输入不正确。
注释 user_class: UserSchemaType
声明 user_class
应该是 BaseModel
的实例,例如 user_class: UserSchemaType = User(...)
.
但是,您分配的是类型,而不是实例:user_class: UserSchemaType = User
。
正确的注释是 user_class: type[UserSchemaType]
或者,根据您的 python 版本,您需要使用 from typing import Type
,然后是 user_class: Type[UserSchemaType] = User
。
如果您在使用不正确的类型注释时检查 test_app_settings.__fields__
,您会发现 user_class
不存在。 Pydantic 覆盖 __setattr__
,默认情况下它会检查属性名称是否在 __fields__
中,如果没有找到,它会抛出您遇到的错误。
我很快就找到了 __getattr__
的实现,似乎 pydantic 没有重新实现它,所以你的属性访问检查(test_app_settings.user_class
在你的例子中输入 105)是受制于正常的 python MRO 属性查找,这就是它在那里找到它的原因。
顺便说一句,在您的示例中使用 TypeVar
并没有真正让您受益,因为 class 不是通用的。您可以轻松地使用 user_class: type[BaseModel] = User
.
我正在使用 Pydantic 进行设置管理,现在我遇到了一个问题。
假设我对多个环境有不同的设置 class。现在我想使用 class 作为不同设置环境的字段属性,并在测试期间更改它们。问题是我能够获取 class,但没有在执行期间明确设置它。这是一个简短的例子:
In [96]: from typing import TypeVar
In [97]: UserSchemaType = TypeVar("UserSchemaType", bound=BaseModel)
In [98]: from pydantic import BaseModel
In [99]: class User(BaseModel):
...: id: str
...:
In [100]: class CustomUser(BaseModel):
...: id: str
...: first_name: str
...: last_name: str
...:
In [101]: class AppSettings(BaseSettings):
...: # some settings
...: foo: str = 'foo'
...:
...: user_class: UserSchemaType = User
...:
In [102]: class TestAppSettings(AppSettings):
...: # some settings
...: pass
...:
In [103]: test_app_settings = TestAppSettings()
In [104]: test_app_settings.dict()
Out[104]: {'foo': 'foo'}
In [105]: test_app_settings.user_class
Out[105]: __main__.User
In [106]: test_app_settings.user_class = CustomUser
ValueError: "TestAppSettings" object has no field "user_class"
AppSettings.user_class
输入不正确。
注释 user_class: UserSchemaType
声明 user_class
应该是 BaseModel
的实例,例如 user_class: UserSchemaType = User(...)
.
但是,您分配的是类型,而不是实例:user_class: UserSchemaType = User
。
正确的注释是 user_class: type[UserSchemaType]
或者,根据您的 python 版本,您需要使用 from typing import Type
,然后是 user_class: Type[UserSchemaType] = User
。
如果您在使用不正确的类型注释时检查 test_app_settings.__fields__
,您会发现 user_class
不存在。 Pydantic 覆盖 __setattr__
,默认情况下它会检查属性名称是否在 __fields__
中,如果没有找到,它会抛出您遇到的错误。
我很快就找到了 __getattr__
的实现,似乎 pydantic 没有重新实现它,所以你的属性访问检查(test_app_settings.user_class
在你的例子中输入 105)是受制于正常的 python MRO 属性查找,这就是它在那里找到它的原因。
顺便说一句,在您的示例中使用 TypeVar
并没有真正让您受益,因为 class 不是通用的。您可以轻松地使用 user_class: type[BaseModel] = User
.