У меня есть модель, определенная следующим образом;
class SomeModel(BaseModel):
name: str
class SomeOtherModel(BaseModel):
name: int
class MyModel(BaseModel):
items: List[Union[SomeModel, SomeOtherModel]]
@validator("items", always=True)
def validate(cls, value):
by_type = list(filter(lambda v: isinstance(v, SomeModel), value))
if len(by_type) < 1:
raise ValueError("we need at least one SomeModel")
Подмодели не важны, по сути мне нужен список различных подмоделей, и этот список должен содержать хотя бы одну из первого типа. Все хорошо.
В другом месте моего кода я имею в виду эту модель (контекст: для целей сохранения пользовательских настроек не имеет значения для этого вопроса).
class ComposerModels(BaseModel):
user: List[MyModel] = []
system: List[MyModel] = []
class ComposerSettings(BaseModel):
models: ComposerModels
class UserSettings(BaseModel):
composer: ComposerSettings
Моя программа должна иметь возможность сохранять новые модели в модели UserSettings, примерно так;
my_model = MyModel(items=[SomeModel(name = "a"), SomeOtherModel(name=1)])
user_settings = UserSettings(
composer=ComposerSettings(
models=ComposerModels(
user=[my_model]
)
)
)
Однако это вызывает ошибку;
Traceback (most recent call last):
File ".../pydantic/shenanigans.py", line 38, in <module>
user=[my_model]
File "pydantic/main.py", line 342, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for ComposerModels
user -> 0
we need at least one SomeModel (type=value_error)
Вот тут-то это и становится странным.
По какой-то причине при попытке создать экземпляр UserSettings он проходит проверку поля items внутри MyModel, но вместо того, чтобы передать список подмоделей, как ожидалось, каким-то образом вместо этого он получает список экземпляров MyModel. Очевидно, что это не проходит проверку и вызывает ошибку, указанную выше. Однако, если я закомментирую код проверки, он сработает. Никаких ошибок, и модель пользовательских настроек содержит ожидаемый MyModel.
Я не могу просто отключить эту проверку, она мне нужна где-то в моей программе... Есть идеи, что здесь происходит? Я в тупике...
Я использую Python 3.7 с pydantic 1.10 на Centos 7. Я не могу легко обновить какие-либо версии, потому что моя компания не верит в DevOps, поэтому, если это известная ошибка с pydantic в этой версии, мне придется подумать чего-то другого.






Должно быть, это какая-то ошибка с pydantic v1.10. Неудивительно, что они упразднили validator в v2.
Я сделал небольшую модификацию, чтобы все работало. Думаю, для вас не составит труда инициализировать класс UserSettings с пустыми пользователем и системой.
my_model = MyModel(items=[SomeModel(name = "a"), SomeOtherModel(name=1)])
user_settings = UserSettings(
composer=ComposerSettings(
models=ComposerModels(
)
)
)
user_settings.composer.models.user = [my_model]
print(user_settings.__dict__)
Надеюсь, это тот результат, которого вы ожидали:
Используйте другое имя функции для валидатора. Метод validate — это существующий метод BaseModel, и вы его переопределяете. Всякий раз, когда создается новый объект, pydantic проверяет поля с соответствующими типами, используя метод validate модели.
Поскольку вы переопределили метод validate, он будет использоваться для проверки модели.
Поток кода будет следующим:
my_model = MyModel(items=[SomeModel(name = "a"), SomeOtherModel(name=1)])
user_settings = UserSettings(
composer=ComposerSettings(
models=ComposerModels(
user=[my_model] # Validate user with List[MyModel]
)
)
)
Теперь, чтобы проверить user, сначала нужно проверить my_model с помощью MyModel
Внутри он вызовет что-то похожее на MyModel.validate(my_model) (вы можете проверить точный источник здесь https://github.com/pydantic/pydantic/blob/547925887071a8cedf4642c4eb16ed749bf802cc/pydantic/v1/main.py#L1074), и поскольку вы переопределить метод validate, для которого проверка isinstance(v, SomeModel) не будет удовлетворена my_model, и она завершится неудачно и вызовет исключение..
Поэтому решение состоит в том, чтобы просто использовать другое имя функции.
class MyModel(BaseModel):
items: List[Union[SomeModel, SomeOtherModel]]
@validator("items", always=True)
def validate_items(cls, value): # Changed to validate_items
by_type = list(filter(lambda v: isinstance(v, SomeModel), value))
if len(by_type) < 1:
raise ValueError("we need at least one SomeModel")
Это будет работать так, как ожидалось.
Святой Бог из машины, ты только что спас мне жизнь
сегодня я пробовал pydantic 2.5.3 (потому что мы всего лишь py37), и похоже, что это на самом деле ошибка в pydantic 1.10. Однако, как уже упоминалось, обновление pydantic будет огромной проблемой... поэтому альтернативное решение было бы идеальным.