SqlAlchemy 2.0: проблемы с использованием Mapped_column() со столбцами Computed() в определениях таблиц ORM (не работает с MappedAsDataclass)

(Примечание: в соответствии с призывом Stack Overflow обогатить сообщество, отвечая на собственные вопросы, особенно после вкладывая значительные усилия в устранение неполадок, я предоставляю ответ ниже. Я с радостью приветствую дальнейшие комментарии, дополнительные идеи или альтернативные подходы! Я бы также хотел признательность Ф. Казелли за ценную помощь в Форум SQLAlchemy, который помог мне сузить поиск. за решение.)

Здравствуйте, я пытаюсь создать столбец COMPUTED непосредственно в определении ORM, используя mapped_column() в SqlAlchemy 2.0, но мне это не удалось (я проверил Google и попытался найти ответ в документации).

Я знаю, что для базовой модели достаточно объявить таблицу следующим образом (пример Toy, где "area" — ВЫЧИСЛЯЕМЫЙ столбец):

square = Table(
    "square",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("side", Integer),
    Column("area", Integer, Computed("side * side")),
)

Это эквивалентно следующему оператору SQL DDL (Postgresql >12):

CREATE TABLE square (
    id SERIAL NOT NULL,
    side INTEGER,
    area INTEGER GENERATED ALWAYS AS (side * side) STORED,
    PRIMARY KEY (id)
)

Однако, пытаясь сделать эквивалент в ORM, используя Mapped[] и mapped_column(), я не могу найти решение.

Я предполагал, что что-то вроде следующего может сработать, но это не так:

class Square(Base):
    ...
    area: Mapped[int] = mapped_column(Computed("side*side"))

До этого я пробовал и другие конфигурации, например, передачу Computed() как server_default= и использование FetchedValue, но во всех случаях возникали ошибки.

Я также пришел к @hybrid_property, но понимаю, что это не то же самое (поскольку мне нужно, чтобы вычисляемый столбец был частью определения DDL таблицы, а @hybrid_property, похоже, оставляет вычисления во время выбора, но не как DDL).

Я был бы признателен, если бы кто-нибудь помог мне правильно использовать mapped_column() с Computed()!

Просто для справки в будущем (и, возможно, чтобы помочь в поиске решения кому-то с похожим случаем), ошибка, которую я получил, когда не включил init=False для столбца Computed() в класс, производный от MappedAsDataclass, выглядит следующим образом: raise exc.InvalidRequestError (sqlalchemy.exc.InvalidRequestError: Python dataclasses error encountered when creating dataclass for 'Square': TypeError("non-default argument 'area' follows default argument")

A.Sommerh 28.04.2024 22:02
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
174
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Мы обнаружили, что недостающей частью был только параметр:

init=False

Итак, чтобы выполнить следующую работу:

area: Mapped[int] = mapped_column(Computed("side*side"))

его необходимо изменить на:

area: Mapped[int] = mapped_column(Computed("side*side"), init=False)

Почему не работала исходная форма?

Оказывается, мой класс Base является производным от MappedAsDataclass, а создание метода __init__() класса данных не было обработано должным образом, как того требует этот тип класса Base.

По-видимому, механизм MappedAsDataclass не исключает автоматически столбец Computed() из метода __init__(), поэтому мы должны явно установить init=False в определении mapped_column().

Краткое содержание

Подводя итог, в моем случае решением было объявить столбец area следующим образом:


class Base(MappedAsDataclass, DeclarativeBase):
    pass

class Square(Base):
    ...
    # for computed column, use init=False if Base is a MappedAsDataclass
    area: Mapped[int] = mapped_column(Computed("side*side"), init=False) 
    ...

Особая благодарность

Спасибо Ф. Казелли за помощь в сужении поиска решения в форум sqlalchemy!!

Другие вопросы по теме