Добавление поведения класса getattr в базовую модель pydantic v2Python

Программы на Python
Ответить Пред. темаСлед. тема
Anonymous
 Добавление поведения класса getattr в базовую модель pydantic v2

Сообщение Anonymous »

У меня есть библиотека, которая по сути является разновидностью ORM. В рамках этого у меня есть собственный класс «Модель», который конечные пользователи библиотеки должны создать подклассы для создания моделей, которые будут сохраняться в базе данных nosql.
Моя база Класс модели наследуется от pydantic.BaseModel. Одной из особенностей этой библиотеки является то, что вы можете создавать фильтры для запросов к базе данных с относительно простым синтаксисом.
Например, скажем, у меня есть собственный класс base.Model и пользователь создает такую ​​модель:

Код: Выделить всё

class Person(base.Model):
first_name: str
last_name: str
age: int
Тогда можно будет создать объект фильтра, выполнив что-то вроде этого:

Код: Выделить всё

filter_ = (Person.age > 10) & (Person.first_name == "Bill")
В pydantic V1 я сделал это, добавив такой метакласс:

Код: Выделить всё

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
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Python»