Как реализовать функцию отмены с помощью Python / Django - PullRequest
12 голосов
/ 18 декабря 2010

У меня есть приложение Django, где я разрешаю пользователю импортировать CSV-файл с контактными данными (номер участника, имя, фамилия и т. Д.).

Когда они импортируют файл, приложение проверяет базу данных на предмет соответствия и либо: 1) вставляет новую запись, если совпадения не существует, либо 2) обновляет существующие данные новыми данными.

Мой вопрос таков: как лучше всего реализовать функцию отмены, используя Django или прямой Python, чтобы пользователь мог отменить операцию импорта и вернуть несколько записей обратно в их исходное состояние?состояние?

Мои первоначальные мысли состоят в том, чтобы создать такую ​​таблицу (псевдокод):

Table HISTORY
   unique_id
   record_affected_id
   old_value
   new_value

Затем, если пользователь нажимает «Отменить», я могу найти уникальный_идентификаторсвязать с их транзакцией и установить для каждой записи, на которую воздействует эта транзакция, значение old_value.

Мне интересно, есть ли более простой способ сделать это, которого мне не хватает, или кто-нибудь сталкивался с чем-то подобным.

Ответы [ 3 ]

10 голосов
/ 18 декабря 2010

Взгляните на django-reversion.Он обеспечивает контроль версий для моделей Django.Может быть легко добавлено к существующему проекту.

Он не использует "текущий" подход указателя.Вместо этого он сериализует объект при каждом сохранении и сохраняет его в отдельной модели Version с общим внешним ключом, указывающим на этот объект.(Поля отношений по умолчанию сериализуются как первичные ключи.) Кроме того, это позволяет гибко группировать Version s в Revision s.

Таким образом, вы можете сделать что-то подобное:

  • Когда пользователь загружает CSV, просто сохраните изменения как обычно, но добавьте декоратор @revision.create_on_success к функции, которая выполняет импорт, чтобы любые изменения в записях, выполненных этой функцией, были сохранены в одной ревизии.
  • Когда пользователь нажимает «Отменить», вы просто возвращаете последнюю версию.

Вот как это можно сделать: *

@revision.create_on_success
def import_csv(request, csv):
    # Old versions of all objects save()d here will
    # belong to single revision.

def undo_last_csv_import(request):
    # First, get latest revision saved by this user.
    # (Assuming you create revisions only when user imports a CSV
    # and do not version control other data.)
    revision = Revision.objects.filter(user=request.user)\
        .order_by('-date_created')[0]
    # And revert it, delete=True means we want to delete
    # any newly added records as well
    revision.revert(delete=True)

Это зависит от того факта, чтовы создаете ревизии только тогда, когда пользователь импортирует CSV.Это означает, что если вы планируете также управлять версиями других данных, вам нужно будет внедрить некоторый флаг, с помощью которого вы можете получать записи, затронутые последним импортом.Затем вы можете получить запись по этому флагу, получить последнюю сохраненную версию и вернуть всю ревизию, к которой принадлежит версия.Вот так ::

def undo_last_csv_import(request):
    some_record = Record.objects.by_user(request.user).from_the_last_import()[0]
    latest_saved_version_of_some_record = Version.objects.get_for_date(
        some_record,
        datetime.now(), # The latest saved Version at the moment.
        )
    # Revert all versions that belong to the same revision
    # as the version we got above.
    latest_saved_version_of_some_record.revision.revert()

Это не красивое решение, наверняка есть способы сделать это лучше с этим приложением.Я рекомендую взглянуть на код, чтобы лучше понять, как работает django-reversion - очень хорошо задокументировано, не удалось найти функцию без строки документации.^ _ ^ d

(Документация тоже хороша, но, как оказалось, немного вводит меня в заблуждение, т. е. пишут Version.objects.get_for_date(your_model, date), где your_model на самом деле является экземпляром модели.)

Обновление: Активно поддерживается реверсия django, так что не стоит сильно полагаться на приведенный выше код, а лучше просмотрите их wiki о том, как управлять версиями и ревизиями вне администратора django.Например, комментарии к редакции уже поддерживаются, что может немного упростить ситуацию.

3 голосов
/ 18 декабря 2010

Вам нужен контроль версий, и проблема не в Python или Django, а в том, как создать базу данных для этого. Одним из распространенных способов является хранение документов с уникальными идентификаторами и отслеживание которых является «текущим». Отмена - это просто вопрос возврата «текущего» указателя к более старой версии. Это, насколько я понимаю, то, что вы делаете.

Хотя это обычный способ сделать это, я не знаю, лучший ли это. Я никогда не видел другого пути, который может означать, что он лучший, или что лучший способ неочевиден. : -)

Выполнение этого в Django в общих чертах, вероятно, является трудной проблемой, но будет проще, если вы заставите свое приложение Django поддерживать его специальным образом.

Затем вы попадаете в забавные (не) проблемы, например, как редактировать вещи в «будущей» ревизии, а затем публиковать сразу целый набор документов в виде поэтапного контента. Но, надеюсь, вам это не нужно. : -)

1 голос
/ 18 декабря 2010

Ваша таблица истории выглядит хорошо, за исключением того, что вам не нужно поле new_value для выполнения отмены. И да, это то, как часто применяется «отмена» (другой альтернативой является подход Леннарта по введению номера версии во все записи). Преимущество отдельной таблицы журнала состоит в том, что вам не нужно иметь дело с номером версии в обычных запросах.

...