2015-11-24 3 views
1

У меня есть post_save, прикрепленный к модели, которая загружает значительный объем информации через ответ json от внешнего API при его создании. К сожалению, это занимает достаточно много времени, чтобы герою время, прежде чем он когда-либо имеет шанс закончить.Как запустить Django post_save сигнал в качестве фонового процесса?

После нескольких исследований кажется, что лучший способ сделать это - фоновый процесс.

Каким будет асинхронный запуск сигнала post_save?

После прочтения сельдерея это кажется слишком тяжелым для одного процесса. Есть ли другие методы, которым доверяют?

from django.db.models.signals import post_save 
import urllib2 
import json 

class Foobar(models.Model): 
    # ... fields ... # 

def foobar_post_save(sender, instance, created, *args, **kwargs): 
    """Load info from external API 

    data 
    ---- 
    sender - The model class. (Foobar) 
    instance - The actual instance being saved. 
    created - Boolean; True if a new record was created. 
    *args, **kwargs - Capture the unneeded `raw` and `using`(1.3) arguments. 
    """ 

    if created: 
     url_to_open = <api_url> 
     resp = urllib2.urlopen(url_to_open) 
     data = json.loads(resp.read()) 
     # ... load data ... # 

post_save.connect(foobar_post_save, sender=Foobar) 

ответ

2

я в конечном итоге найти ответ с rq, который имеет хороший Heroku documentation

и реализована следующим образом:

from django.db.models.signals import post_save 
import urllib2 
import json 
from rq import Queue 
from worker import conn 

q = Queue(connection=conn) 

class Foobar(models.Model): 
    # ... fields ... # 

    def load_data(self): 
     url_to_open = <api_url> 
     resp = urllib2.urlopen(url_to_open) 
     data = json.loads(resp.read()) 
     # ... load data ... # 

def foobar_post_save(sender, instance, created, *args, **kwargs): 
    """Load info from external API 

    data 
    ---- 
    sender - The model class. (Foobar) 
    instance - The actual instance being saved. 
    created - Boolean; True if a new record was created. 
    *args, **kwargs - Capture the unneeded `raw` and `using`(1.3) arguments. 
    """ 

    if created: 
     q.enqueue(instance.load_data, timeout=600) 

post_save.connect(foobar_post_save, sender=Foobar) 
1

Я не уверен на 100%, что это именно то, что вы хотите, но я сделал аналогичную вещь, поэтому я думаю, что это сделает то, что вам нужно.

import multiprocessing 

proc = multiprocessing.Process(
    target = foobar_post_save,   #the function to be run 
    args = (sender,instance,created), #the arguments to pass to the function 
    name="foobar post save")    #name not strictly necessary 
proc.start()  #starts the process 
proc.join(60) #wait until this is done; optional time limit is in seconds 

Более подробно об этом смотрите здесь: Python 3 multiprocessing module docs.

+0

Я не уверен, что это может работать в этой ситуации. Поскольку django обрабатывает вызов сигналов post_save где-то в 'models.Model.save()' Я думаю, мне пришлось бы перегрузить этот метод для вызова '.start()' в процессе. Я надеялся, что будет что-то менее уродливое. – Ben

+0

Ну, я не использую Heroku, и эта аналогичная вещь, которую я сделал, не включала сигнал post_save, поэтому я не думаю, что могу помочь вам намного дальше. Сожалею. –

+0

Благодарим вас за ответ – Ben

0

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

+0

Я не очень хорошо знаком с сельдереем, но, как я уже говорил в этом вопросе, кажется довольно тяжелым для выполнения одного процесса, мне было интересно, было ли что-то проще? – Ben

+0

Я предполагаю, что проблема заключается в том, что длительные процессы в идеале должны быть исключены из цикла запросов-ответов, чтобы улучшить опыт для пользователя и остановить процесс сервера, который будет задерживаться медленной задачей. Тот факт, что вы набираете тайм-аут, говорит о том, что он слишком длинный, чтобы считаться разумным для запроса-ответа. Там могут быть другие более простые очереди, но я бы пошел с этим. – jvc26

0

Другая идея использования Celery или что-то подобное, чтобы запустить задачу в фоновом режиме. Итак, вы создаете новую задачу на сельдерей после post_save, а затем вы можете контролировать эту задачу, чтобы остановить/приостановить/получить сигнал успеха/и т.д.

Смежные вопросы