Моя база Класс модели наследуется от pydantic.BaseModel. Одной из особенностей этой библиотеки является то, что вы можете создавать фильтры для запросов к базе данных с относительно простым синтаксисом.
Например, скажем, у меня есть собственный класс base.Model и пользователь создает такую модель:
Код: Выделить всё
class Person(base.Model):
first_name: str
last_name: str
age: int
Код: Выделить всё
filter_ = (Person.age > 10) & (Person.first_name == "Bill")
Код: Выделить всё
class Model(pydantic.BaseModel, metaclass=utils.MetaGetFilterPydantic):
...
Код: Выделить всё
class MetaGetFilterPydantic(MetaGetFilter, type(pydantic.BaseModel)):
pass
Код: Выделить всё
class MetaGetFilter(type):
def _get_full_annotations(cls) -> collections.ChainMap[str, Any]:
annotations_ = [cls.__annotations__]
for parent in cls.__mro__:
if parent is pydantic.BaseModel:
break
annotations_.append(parent.__annotations__)
return collections.ChainMap(*annotations_)
def __getattr__(cls, item: str) -> MyFilterObject:
if item not in cls._get_full_annotations():
raise AttributeError(f"type object '{cls.__name__}' has no attribute '{item}'")
return MyFilterObject(...)
Проблема в том, что в pydantic V2 это полностью нарушено. Я видел, что pydantic V2 использует pydantic._internal._model_construction.ModelMetaclass в качестве метакласса для создания новых моделей, и я поигрался с тем, чтобы позволить этому моему метаклассу быть его подклассом и перезаписать его __getattr__.
Если я сделаю что-то подобное в своем метаклассе (после создания подкласса метакласса модели pydantic выше):
Код: Выделить всё
def __getattr__(cls, item):
try:
return super().__getattr__(item)
except AttributeError:
if not cls.__pydantic_complete__ or cls is base.Model:
raise
attrs = (
cls._get_full_annotations()
)
if item not in attrs:
raise
return MyFilterObject(...)
Длинный вопрос: есть ли какой-нибудь безопасный способ добавить поведение к доступу к объектам класса pydantic в V2? Похоже, что pydantic теперь полагается на __getattr__, вызывающий AttributeError
Я думал о потенциальном способе узнать, когда объект действительно инициализируется, но __pydantic_complete__ оказалось недостаточно
Подробнее здесь: https://stackoverflow.com/questions/772 ... -basemodel