Как заставить django дать ответ HTTP, прежде чем продолжить, чтобы завершить задачу, связанную с запросом? - PullRequest
6 голосов
/ 07 июля 2011

В моем API поршня django я хочу выдать / вернуть HTTP-ответ клиенту перед вызовом другой функции, которая займет довольно много времени. Как сделать так, чтобы yield выдавал HTTP-ответ, содержащий желаемый JSON, а не строку, относящуюся к созданию объекта генератора?

Мой метод обработки поршня выглядит так:

def create(self, request):
    data = request.data 

    *other operations......................*

    incident.save()
    response = rc.CREATED
    response.content = {"id":str(incident.id)}
    yield response
    manage_incident(incident)

Вместо ответа, который я хочу, например:

   {"id":"13"}

Клиент получает строку, подобную этой:

 "<generator object create at 0x102c50050>"

EDIT:

Я понимаю, что использование yield было неправильным путем, в сущности, я пытаюсь добиться того, чтобы клиент получил ответ сразу, прежде чем сервер перейдет к дорогостоящей функции manage_incident ()

Ответы [ 3 ]

9 голосов
/ 07 июля 2011

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

Использование:

@postpone
def long_process():
    do things...

def some_view(request):
    long_process()
    return HttpResponse(...)

А вот код, чтобы заставить его работать:

import atexit
import Queue
import threading

from django.core.mail import mail_admins


def _worker():
    while True:
        func, args, kwargs = _queue.get()
        try:
            func(*args, **kwargs)
        except:
            import traceback
            details = traceback.format_exc()
            mail_admins('Background process exception', details)
        finally:
            _queue.task_done()  # so we can join at exit

def postpone(func):
    def decorator(*args, **kwargs):
        _queue.put((func, args, kwargs))
    return decorator

_queue = Queue.Queue()
_thread = threading.Thread(target=_worker)
_thread.daemon = True
_thread.start()

def _cleanup():
    _queue.join()   # so we don't exit too soon

atexit.register(_cleanup)
2 голосов
/ 07 июля 2011

Возможно, вы могли бы сделать что-то вроде этого (будьте осторожны):

import threading
def create(self, request):
    data = request.data 
    # do stuff...
    t = threading.Thread(target=manage_incident,
                         args=(incident,))
    t.setDaemon(True)
    t.start()
    return response

Кто-нибудь пробовал это? Это безопасно? Я думаю, что это не так, в основном из-за проблем параллелизма, но также из-за того, что если вы получаете много запросов, вы также можете получить много процессов (так как они могут работать некоторое время) , но это может стоить того.

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

Я не думаю, что Django создан для параллелизма или очень трудоемких операций.

Редактировать

Кто-то пробовал, похоже, работает.

0 голосов
/ 07 июля 2011

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

def create(self, request):
  return HttpResponse(real_create(request))

EDIT:

Так как у вас, похоже, возникают проблемы ... визуализация этого ...

def stuff():
  print 1
  yield 'foo'
  print 2

for i in stuff():
  print i

выход:

1
foo
2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...