post_save в django для немедленного обновления экземпляра

Пытаюсь сразу обновить запись после сохранения. Этот пример может показаться бессмысленным, но представьте, что нам нужно использовать API после сохранения данных, чтобы получить дополнительную информацию и обновить запись:

def my_handler(sender, instance=False, **kwargs):
    t = Test.objects.filter(id=instance.id)
    t.blah = 'hello'
    t.save()

class Test(models.Model):
    title = models.CharField('title', max_length=200)
    blah = models.CharField('blah', max_length=200)

post_save.connect(my_handler, sender=Test)

Таким образом, предполагается, что в поле 'extra' после каждого сохранения должно быть установлено значение 'hello'. Правильный? Но это не работает.

Любые идеи?


person givp    schedule 28.10.2009    source источник
comment
Может быть, вы могли бы описать, как это не работает? На первый взгляд кажется, что это будет бесконечный цикл, поскольку post_save вызывает save, который должен вызывать post_save и т. Д. Может быть, Django предотвращает рекурсию?   -  person Ned Batchelder    schedule 29.10.2009
comment
Я вижу там бесконечный цикл. После того, как t.save () будет отправлен сигнал post_save, угадайте, какая функция будет вызвана ...   -  person stefanw    schedule 29.10.2009
comment
о, у меня создалось впечатление, что Django не позволит второму сохранению снова запустить post_save? Думаю, нет. В этом случае ты прав. Это был бы бесконечный цикл. Но я не вижу петли или чего-то подобного.   -  person givp    schedule 29.10.2009
comment
Вероятно, это глупый вопрос, но разве вы не можете сначала вызвать API, а затем сохранить?   -  person Matt Baker    schedule 29.10.2009
comment
вы имеете в виду pre_save? Я должен сделать это на уровне модели, потому что я не могу каким-либо образом изменять админку Django для этого проекта.   -  person givp    schedule 29.10.2009
comment
Почему вы просто не обновляете метод save для выполнения ; self.blah= 'hello'; super( Test, self ).save( *args, **kw )? Что не так с переопределением save()?   -  person S.Lott    schedule 29.10.2009
comment
Если вы меняете перекодировку .update() вместо save(). Чтобы избежать петли. Поскольку update () не вызывает сигнал post_save.   -  person Brandon Bertelsen    schedule 26.02.2013


Ответы (2)


Когда вы обнаружите, что используете сигнал post_save для обновления объекта класса отправителя, скорее всего, вы должны вместо этого переопределить метод сохранения. В вашем случае определение модели будет выглядеть так:

class Test(models.Model):
    title = models.CharField('title', max_length=200)
    blah = models.CharField('blah', max_length=200)

    def save(self, force_insert=False, force_update=False):
        if not self.blah:
            self.blah = 'hello'
        super(Test, self).save(force_insert, force_update)
person ozan    schedule 29.10.2009
comment
Если он делает это с моделями администратора, post_save - лучшее решение, чем создание подкласса существующих моделей администратора и переопределение сохранения. - person Paul McMillan; 29.10.2009
comment
using=False требуется в списке аргументов метода save в Django 1.3 и выше - person eviltnan; 20.07.2012
comment
Переопределение метода сохранения не всегда подходит, потому что у нас нет первичного ключа, пока мы не вызовем save. Поэтому, если вам нужно работать с PK экземпляра, кажется, что только вариант post_save - person chhantyal; 27.03.2015
comment
Перспективный способ переопределить метод save с включением всех необходимых аргументов: def save(self, *args, **kwargs): - person yndolok; 21.04.2015

Разве обработчик post_save не берет экземпляр? Почему вы фильтруете с его помощью? Почему бы просто не сделать:

def my_handler(sender, instance=False, created, **kwargs):
  if created:
     instance.blah = 'hello'
     instance.save()

Ваш существующий код не работает, потому что он зацикливается, а Test.objects.filter(id=instance.id) возвращает набор запросов, а не объект. Чтобы получить один объект напрямую, используйте Queryset.get(). Но здесь этого делать не нужно. Созданный аргумент не дает ему зацикливаться, так как он устанавливает его только в первый раз.

В общем, если вам абсолютно не нужно использовать сигналы post_save, вы все равно должны переопределить метод save () вашего объекта.

person Paul McMillan    schedule 28.10.2009
comment
Я на самом деле пробовал это тоже безрезультатно. но если люди выше правы, я все равно не могу этого сделать, так как он просто застрянет в цикле post_save. - person givp; 29.10.2009
comment
Попробуйте использовать созданный флаг. Я думаю, что это должно решить вашу проблему, потому что created не установлен в последующих циклах. - person Paul McMillan; 29.10.2009
comment
Возможно, вы захотите посмотреть на этот вопрос для получения дополнительной информации о том, когда использовать сигналы, а когда переопределить сохранение: stackoverflow.com/questions/170337/ - person Paul McMillan; 29.10.2009
comment
дох! У меня было значение истинности теста для созданного флага, установленного неправильно. Теперь должно работать ... - person Paul McMillan; 29.10.2009
comment
В исходном плакате не упоминается, что он хочет, чтобы обработчик вызывался только при создании новой записи. Таким образом, использование флага created не решает проблемы, если обработчик должен вызываться также при обновлении. (Сам пытаюсь найти решение подобной проблемы.) - person jholster; 22.11.2010
comment
jholster: затем запускать при каждом запуске, игнорируя аргумент created. - person Paul McMillan; 23.11.2010