Как реализовать версию, поддерживающую только приложение, в 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, диспетчер миграции,...). Вместо этого вы можете подтолкнуть эту сложность к слою и позволить другому виду БД обрабатывать его для вас.