1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors-baserow

Клонировать/Скачать
undo-redo-guide.md 14 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 26.06.2025 16:02 92285f5

Руководство по Отмене/Повтору

Действия

ActionType — это класс, который определяет, как выполнять do, undo и redo конкретное действие в Baserow. Он может свободно использовать Handlers для выполнения логики, но почти наверняка не должен вызывать другие ActionType, если только это не какой-то вид meta ActionType, если таковой когда-либо появится. ActionTypes будут извлекаться из реестра по типу и запускаться методами API (например, action_type_registry.get_by_type(DeleteWorkspaceAction).do(user, workspace_to_delete)).

  1. В backend/src/baserow/core/actions/registries.py есть action_type_registry, который можно использовать для регистрации ActionType.
  2. ActionType должен реализовывать методы do/undo/redo.
    1. do выполняет действие, когда пользователь запрашивает его выполнение, и также должен сохранять модель Action с помощью cls.register_action.
    2. undo должен отменять действие, выполненное методом do. Он не должен сохранять никакие модели Action.
    3. redo должен повторять действие после его отмены методом undo. Он также не должен сохранять никакие модели Action.
  3. ActionType должен реализовывать Params dataclass, в котором он будет хранить любые параметры, необходимые для отмены или повтора действия. Экземпляр этого dataclass должен быть предоставлен методу cls.register_action в методе do, и он будет сериализован в JSON и сохранен в таблице Action. Когда вызывается метод redo или undo, этот dataclass будет создан снова из JSON в строке Action и предоставлен функции.

Краткое описание Таблицы Действий

См. baserow.core.action.models.Action для более подробной информации.

id (serial) user_id (fk к таблице пользователей, nullable) session (text nullable) category (text) created_on (auto_now_add DateTimeField) type (text) params (JSONB) undone_at (nullable DateTimeField) error (text nullable)
1 2 'some-uuid-from-client' 'root' datetime 'workspace_created' '{created_workspace_id:10}' null null

ActionHandler и Эндпоинты Отмены/Повтора

ActionHandler имеет методы undo и redo, которые можно использовать для запуска отмены/повтора действия для пользователя. Существуют два соответствующих эндпоинта в /api/user/undo и /api/user/redo, которые вызывают ActionHandler. Для запуска отмены/повтора нам нужны три куска информации:

  1. Пользователь, запускающий отмену/повтор, чтобы проверить, есть ли у него разрешения на отмену/повтор действия. Например, пользователь может пытаться повторить удаление рабочего пространства, но если его забанили в этом рабочем пространстве за это время, он должен быть предотвращен от повтора.
  2. client session id. Каждый раз, когда пользователь выполняет действие в Baserow, мы проверяем заголовок ClientSessionId. Если он установлен, мы связываем действие с этим ClientSessionId. Когда пользователь затем переходит к отмене или повтору, он также предоставляет этот заголовок, и мы позволяем ему отменять/повторять только действия с совпадающим ClientSessionId. Это позволяет иметь разные истории отмены/повтора для каждой вкладки, открытой пользователем, так как каждая вкладка генерирует уникальный ClientSessionId.
  3. category. Каждый раз, когда действие выполняется в Baserow, мы связываем его с определенной категорией. Это буквально просто текстовое поле в модели Action со значениями, такими как root или table10 или workspace20. Категория действия описывает, в какой логической части Baserow было выполнено действие. Реализация ActionType решает, какую категорию установить при вызове cls.register_action. Когда происходит отмена/повтор, веб-фронтенд отправляет категории, на которые пользователь смотрит в данный момент. Например, если у меня открыта таблица 20 с рабочим пространством 6 в боковой панели и я нажимаю отмену/повтор, отправляемая категория будет:
{
  root: true,
  table: 20,
  workspace: 6
}

Отправляя эту категорию в эндпоинт отмены/повтора, мы указываем ему отменить любые действия, выполненные в:

  1. Категории root
  2. Категории таблицы 20
  3. Категории рабочего пространства 6

Например, если я переименовал таблицу 20, то действие table_update будет находиться в категории рабочего пространства 6. Если я затем смотрю на таблицу 20 в интерфейсе и нажимаю отмену, интерфейс отправит категорию рабочего пространства 6 как одну из активных категорий, так как таблица 20 находится в рабочем пространстве 6. Это означает, что я смогу отменить это переименование. Если я переключусь на рабочее пространство 5 и нажму отмену, интерфейс отправит категорию рабочего пространства 5, и я не смогу отменить переименование таблицы 20 до тех пор, пока не переключусь обратно на часть интерфейса, где активна категория рабочего пространства 6.

Пример работы Отмены/Повтора

  1. Пользователь A открывает Таблицу 10, которая находится в Приложении 2 в Рабочем пространстве 1.
    1. При загрузке страницы генерируется и сохраняется в хранилище auth ClientSessionId example_client_session_id (это обычно UUID).
    2. Текущая категория для этой страницы устанавливается в хранилище undoRedo как: {root: true, table_id:10, application_id:2, workspace_id:1}
  2. Пользователь A изменяет имя Таблицы.
    1. Отправляется запрос на эндпоинт обновления таблицы.
      1. В запросе устанавливается заголовок ClientSessionId со значением example_client_session_id.
    2. Эндпоинт API обновления таблицы вызывает action_type_registry.get(UpdateTableAction).do(user, ...).
    3. Изменение выполняется и создается новая запись Action.
      1. UpdateTableAction устанавливает категорию действия как workspace1.
      2. Из запроса извлекается ClientSessionId, и сессия действия устанавливается как example_client_session_id.
      3. Пользователь действия устанавливается как User A.
      4. Старое имя таблицы сохраняется в JSONField модели action.params, чтобы облегчить отмену и повтор.
  3. Пользователь A нажимает «Отмена».
    1. Отправляется запрос на эндпоинт «undo» с данными запроса category, установленными на текущую категорию страницы, открытой пользователем, полученную из хранилища undoRedo (см. выше).
      1. В запросе устанавливается заголовок ClientSessionId со значением example_client_session_id.
    2. Вызывается метод ActionHandler.undo.
      1. Он находит последнее действие для пользователя A в сессии example_client_session_id и в любой из следующих категорий: ["root", "workspace1", "application2", "table10"]. Эти категории были вычислены из параметра категории, переданного эндпоинту.
      2. Находится действие переименования таблицы, так как его сессия совпадает, оно находится в категории workspace, выполнено пользователем A и еще не было отменено (поле undone_at равно null).
      3. Он десериализует параметры последнего действия из таблицы в dataclass параметров действия.
      4. Вызывается метод action_type_registry.get(UpdateTableAction).undo(user, params, action_to_undo).
      5. UpdateTableAction использует параметры для отмены действия.
      6. Поле Action.undone_at устанавливается на значение datetime.now(tz=timezone.utc), указывая на то, что действие теперь отменено.

Что происходит при неудачной отмене/повторе

Представьте ситуацию, когда два пользователя одновременно работают над таблицей:

  1. Пользователь A изменяет ячейку в поле 'date'.
  2. Пользователь A изменяет ячейку в поле 'Name'.
  3. Пользователь B удаляет поле 'name'.
  4. Пользователь A нажимает 'отмена' — в текущей реализации он получает ошибку о том, что отмена не удалась и была пропущена.
  5. Пользователь A нажимает 'отмена' — в текущей реализации первое изменение пользователя A теперь отменяется.

Мы не можем отменить последнее действие пользователя A, так как оно было выполнено для ячейки в удаленном поле 'name'. Что произойдет:

  1. Мы попробуем отменить действие пользователя A, вызвав ActionHandler.undo.
  2. Это вызовет исключение.
  3. В методе ActionHandler.undo мы перехватываем это исключение и:
    1. Сохраняем его в поле ошибки действия.
    2. Отмечаем действие как «отмененное», устанавливая его поле datetime undone_at на значение datetime.now(tz=timezone.utc).
    3. Отправляем пользователю специальную ошибку о том, что отмена не удалась и была пропущена.

Если пользователь затем дважды нажмет повтор, произойдет следующее:

  1. Будет повторено первое действие пользователя A.
  2. Теперь мы пытаемся повторить действие, которое не удалось выполнить ранее. У него установлено поле ошибки. Мы видим эту ошибку и отправляем пользователю сообщение об ошибке: «нельзя повторить из-за ошибки, пропускаем».
  3. Однако мы также удаляем ошибку и отмечаем действие как «повторенное».
  4. Теперь пользователь может снова нажать «отмена», и действие будет попытаться быть отменено второй раз так же, как первый раз. Если пользователь B к этому моменту восстановил удаленное поле, это может теперь сработать!

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/mirrors-baserow.git
git@api.gitlife.ru:oschina-mirror/mirrors-baserow.git
oschina-mirror
mirrors-baserow
mirrors-baserow
develop