Почему я могу получить доступ к объекту во время его сигнала post_save, а не когда я запускаю код в этом сигнале, который вызывает его в другом процессе - PullRequest
14 голосов
/ 16 декабря 2011

Все, у меня проблема с сигналами django.

У меня есть модель В попытке ускорить реагирование на загрузку страниц, я разгружаю некоторую интенсивную обработку, которая должна быть выполнена с помощью вызова второго веб-сервера localhost, который у нас работает, оба используют одну и ту же базу данных. Я вижу поведение, когда вызывающий процесс может получить объект, но вызываемый процесс не может. И порт 80, и порт [порт] указывают на процессы django, работающие на одной базе данных.

В models.py

class A(models.Model):
    stuff...

def trigger_on_post_save( sender, instance, create, raw, **keywords):
    #This line works
    A.objects.get( pk=instance.pk )
    #then we call this
    urlopen( r'http://127.0.0.1:[port]' + 
        reverse(some_view_url, args(instance_pk) ).read()

post_save.connect( trigger_on_post_save, A )

В views.py

def some_view_function( request, a_pk ):
    #This line raises an object_not_found exception
    A.objects.get( pk=a_pk )

Кроме того, после вызова urlopen возникает исключение, объект не существует в базе данных. Насколько я понимаю, post_save вызывался после того, как объект был сохранен и записан в базу данных. Это неправильно?

Ответы [ 4 ]

16 голосов
/ 13 мая 2016

Мы столкнулись с подобной проблемой, и в итоге мы использовали обратный вызов on_commit ( ПРИМЕЧАНИЕ : это возможно только при Django> = 1.9). Таким образом, вы можете сделать что-то вроде:

from django.db import transaction

class A(models.Model):
    stuff...

def trigger_on_post_save( sender, instance, create, raw, **keywords):
    def on_commit():
        urlopen(r'http://127.0.0.1:[port]' + 
                 reverse(some_view_url, args(instance_pk) ).read()
    transaction.on_commit(on_commit)

post_save.connect( trigger_on_post_save, A )

Идея заключается в том, что вы будете вызывать свою конечную точку после транзакции, которая будет зафиксирована, поэтому экземпляр, участвующий в транзакции, будет уже сохранен;).

13 голосов
/ 16 декабря 2011

Я полагаю, что post_save срабатывает после сохранения, но до того, как транзакция будет передана в базу данных.По умолчанию Django фиксирует изменения в базе данных только после завершения запроса.

Два возможных решения вашей проблемы:

  1. Управление транзакциями вручную ,и сгенерируйте пользовательский сигнал после фиксации.
  2. Попросите вашего второго процесса немного подождать, пока запрос не пройдет.

Честно говоря, вся ваша установка кажется немногонемного противноВам, вероятно, стоит заглянуть в Celery для асинхронной очереди задач.

2 голосов
/ 29 декабря 2017

Это хорошее место, чтобы использовать декораторы. Существует несколько расширенная версия ответа Йоанис-гила:

from django.db import transaction
from django.db.models.signals import post_save

def on_transaction_commit(func):
    def inner(*args, **kwargs):
        transaction.on_commit(lambda: func(*args, **kwargs))

return inner

@receiver(post_save, sender=A)
@on_transaction_commit
def trigger_on_post_save(sender, **kwargs):
    # Do things here
1 голос
/ 28 декабря 2015

Была такая же проблема при создании новой модели от администратора django.Переопределенный метод ModelAdmin.save_model для управления транзакциями сработал вручную.

def save_model(self, request, obj, form, change):
    from django.db import transaction
    with transaction.commit_on_success():
       super(ModelAdmin, self).save_model(request, obj, form, change)

    # write your code here
...