Как реализовать версию, поддерживающую только приложение, в SQLAlchemy

Я хотел бы повторно реализовать некоторые из моих существующих моделей SQLAlchemy в хранилище данных только для приложений; append-only означает, что объект обновляется только с помощью инструкций INSERT, а не с помощью операторов UPDATE или DELETE.

Операторы UPDATE и DELETE будут заменены другим INSERT, который увеличивает версию. Будет флаг is_deleted, а вместо DELETE будет создана новая версия с is_deleted=True:

id  | version | is_deleted | name      | description ...
---- --------- ------------ ----------- ---------------
  1 |       1 |          F | Fo        | Text text text.
  1 |       2 |          F | Foo       | Text text text.
  2 |       1 |          F | Bar       | null 
  1 |       3 |          T | Foo       | Text text text.         

Кроме того,

  • Все инструкции SELECT должны быть переписаны только для максимального номера версии для каждого идентификатора, как описано в этом вопросе: PostgreSQL - выборка строки, которая имеет значение Max для столбца
  • Все (уникальные) индексы должны быть переписаны как уникальные с помощью первичного ключа "id", так как каждый идентификатор может присутствовать более одного раза.

Я знаю, как решить большинство из этих проблем, но я борюсь с крючками событий в SQLAlchemy, которые будут обрабатывать определенные вещи, которые необходимо выполнить при обновлении и удалении.

В документации SQLAlchemy уже есть некоторые базовые примеры для управления версиями. versioned rows пример близок к тому, что я хочу, но они не обрабатывают (1) удаление и (2) отношения внешних ключей.

(1) Удаление. Я знаю, что есть поле session.deleted, и я буду перебирать его аналогично тому, как session.dirty повторяется в versioned_rows.py, но как бы я отменил элемент из списка, который будет удален, и создаст новый элемент?

(2) Вышеупомянутый пример касается только отношения родитель-потомок, и способ его выполнения (с истечением срока действия), по-видимому, требует настраиваемого кода для каждой модели. (2.1) Есть ли способ сделать это более гибким? (2.2) можно ли настроить SQLAlchemy relationship() для возврата объекта с max (версией) для данного внешнего ключа?

Ответ 1

Одна полезная вещь, которая может быть агностиком инструмента ORM, может быть "вместо" триггеров. Например, вы можете поймать событие перед обновлением и открыть приращение номера версии с недавно обновленными данными.

Для postgresql они подробно описаны здесь.

Конечно, вам придется иметь изменения модели (на ПК и т.д.).

Кроме того, было бы полезно изучить влияние производительности, так как вам, скорее всего, придется иметь рекурсивный запрос, чтобы получить "последнюю версию" (через уровень представления или в sql-алхимии, где clauses/etc.)

Ответ 2

Как сумасшедший может показаться вам, на самом деле лучше использовать другую базу данных. Вы знаете Datomic?. Одно из фундаментальных различий между традиционной СУБД и этим типом системы заключается в том, что обновление не на месте, а именно, как RDBMS обновляет файлы на диске. Вместо этого все версируется, и вы можете вернуться через все предыдущие версии базы данных для каждого изменения на каждый отдельный ресурс. Кроме того, вы можете легко увидеть состояние всей базы данных в определенный момент времени, просто передав время интереса в качестве параметра. Есть много других интересных преимуществ, и я настоятельно рекомендую взглянуть на некоторые из них, посвященные Rich Hickey, например этот. Это определенно принципиально другой подход к тому, что вы в настоящее время пытаетесь, но нужно подумать, будет ли это усилие преследоваться, борясь с инструментами на каждом шагу, используя их таким образом, чтобы они действительно не были (RDBMS, ORM, диспетчер миграции,...). Вместо этого вы можете подтолкнуть эту сложность к слою и позволить другому виду БД обрабатывать его для вас.