Как отменить удаление в сигнал Джанго - PullRequest
11 голосов
/ 11 апреля 2011

Есть ли способ отменить удаление записи с помощью сигнала django pre_delete?

пример:

def on_delete(sender,**kwargs):
  if not <some condition>:
    #cancel the deletion
 # else continue with the deletion
pre_delete.connect(on_delete,sender=MyModel)

и еще один вопрос: есть ли способ сказать модели ", что перед изменением файла сначала удалите исходный файл ", потому что сейчас я делаю это (см. Код ниже) не уверен, что это лучший способ сделать это.

def on_save(sender,**kwargs):
  obj = kwargs['instance']
  try:
    id = obj.pk
    # find the file
    original_file = sender.objects.get(pk=id)
    # delete the original file before uploading a new file
    original_file.file.delete()
  except ....

pre_save.connect(on_save,sender=ModelWithFileUpload)

(в django 1.2 они автоматически удаляют файл при изменении или удалении, но в django 1.3 они удаляют эту функцию)

Заранее спасибо

Ответы [ 3 ]

1 голос
/ 12 апреля 2011

Я бы попробовал небольшой обходной путь:

def on_delete(sender,**kwargs):
  if not <some condition>:
    raise Exception('Do not delete')#cancel the deletion
 # else continue with the deletion
pre_delete.connect(on_delete,sender=MyModel)

и вид

def on_save(sender,**kwargs):
  obj = kwargs['instance']
  try:
    id = obj.pk
    # find the file
    original_file = sender.objects.get(pk=id)
    # delete the original file before uploading a new file
  except ... :
    # oder exceptions 

  try:
    original_file.file.delete()
  except:
    pass #not deleted

pre_save.connect(on_save,sender=ModelWithFileUpload)

Повышение исключения в сигнале должно тормозить выполнение метода delete () при возврате исключения в место, где оно было вызвано. Вы можете создать свой собственный подкласс Exception, чтобы исключить только определенный тип исключения (вы почти никогда не должны использовать, кроме как без аргументов).

1 голос
/ 18 марта 2016

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

Итак, обо всем по порядку:

  1. Есть ли способ отменить удаление записи с помощью сигнала django pre_delete?

Не совсем, за исключением того, что предложено thedk. И, честно говоря, не должно быть никаких. Зачем? Потому что pre_delete предназначен для действия, которое должно произойти до удаления объекта. Если вы запретите удаление, оно больше не будет pre_delete (обратите внимание на замкнутый круг?)

  1. существует ли способ сказать модели, что перед изменением файла сначала удалите исходный файл?

Да, есть, и вы правильно поняли. Я создал более общий код, который будет работать для любой модели, с которой связаны объекты File (см. Ниже). Тем не менее, вы должны заранее прочитать почему это поведение было удалено в Django 1.3 и посмотреть, влияет ли оно на вашу логику каким-либо образом. В основном это связано с тем, как вы обрабатываете откаты и множественные ссылки на один и тот же файл из разных моделей.

def delete_files_from_instance(instance, field_names):
    for field_name in field_names:
        field_value = getattr(instance, field_name, None)
        if field_value:
            if isinstance(field_value, File):
                try:
                    os.remove(field_value.path)
                except OSError:
                    pass


@receiver(pre_delete)
def on_delete(sender, instance, **kwargs):
    # When an object is deleted, all associated files are also removed
    delete_files_from_instance(instance, sender._meta.get_all_field_names())


@receiver(pre_save)
def on_update(sender, instance, **kwargs):
    # When an object is updated, if any media files are replaced, the old ones should be deleted.
    from_fixture = 'raw' in kwargs and kwargs['raw'] # this prevents errors when loading files from fixtures
    is_valid_app = sender._meta.app_label in VALID_APPS # Define what apps are targeted by your code
    if is_valid_app and not from_fixture:
        try:
            old_instance = sender.objects.filter(pk=instance.id).first()
            if old_instance and old_instance is not None:
                delete_files_from_instance(old_instance, sender._meta.get_all_field_names())
        except LookupError:
            pass

Имейте в виду, что это предполагает, что действие по удалению / обновлению будет успешным. В случае неудачи вы навсегда потеряли файл.

Лучшим подходом будет обработка удаления файлов в сигналах post_save / post_delete или создание задания cron, которое периодически очищает все файлы, на которые больше нет ссылок из базы данных.

0 голосов
/ 11 апреля 2011

Это невозможно при использовании встроенных сигналов Django.Методы send () и send_robust () для сигналов возвращают список из двух кортежей - (получатель, ответ).Таким образом, если у вас есть правильный код для обработки ответов от каждого получателя, возможно, вы могли бы предотвратить некоторые действия, основанные на возвращаемом значении одного обработчика сигнала.

Приложение contrib.comments делает это, позволяялюбой получатель, который возвращает False для «отмены» действия сигнала.См. строки 111-120 :

Однако основной код Django , который выдает сигналы pre_delete, pre_save и т.д., не имеет этой специальной обработки.Все эти сигналы уведомляют получателей о том, что что-то произошло.

...