2010-05-27 11 views
48

Estoy tratando de obtener el "mundo de saludo" de las respuestas de transmisión que funcionan para Django (1.2). Descubrí cómo usar un generador y la función yield. Pero la respuesta aún no está transmitiendo. Sospecho que hay un middleware que se está volviendo loco: ¿tal vez una calculadora ETAG? Pero no estoy seguro de cómo deshabilitarlo. ¿Alguien puede ayudarme?Cómo transmitir una HttpResponse con Django

Aquí está el "hola mundo" de streaming que tengo hasta ahora:

def stream_response(request): 
    resp = HttpResponse(stream_response_generator()) 
    return resp 

def stream_response_generator(): 
    for x in range(1,11): 
     yield "%s\n" % x # Returns a chunk of the response to the browser 
     time.sleep(1) 
+1

@Tomasz: la especificación del protocolo WSGI http://www.python.org/dev/peps/pep-0333/ –

Respuesta

42

Puede desactivar el middleware ETAG utilizando el condition decorator. Eso hará que tu respuesta vuelva a transmitirse a través de HTTP. Puede confirmar esto con una herramienta de línea de comandos como curl. Pero probablemente no sea suficiente para que su navegador muestre la respuesta a medida que se transmite. Para alentar al navegador a que muestre la respuesta mientras se transmite, puede empujar un grupo de espacios en blanco por el conducto para forzar el llenado de sus buffers. Ejemplo a continuación:

from django.views.decorators.http import condition 

@condition(etag_func=None) 
def stream_response(request): 
    resp = HttpResponse(stream_response_generator(), content_type='text/html') 
    return resp 

def stream_response_generator(): 
    yield "<html><body>\n" 
    for x in range(1,11): 
     yield "<div>%s</div>\n" % x 
     yield " " * 1024 # Encourage browser to render incrementally 
     time.sleep(1) 
    yield "</body></html>\n" 
+3

En mis pruebas, el Django GZipMiddleware puede evitar que esto funcione. – Xealot

+1

Sí, espero que muchos middlewares puedan meterse con eso, así que si no funciona, intente desactivar todos sus middlewares y reajustarlos incrementalmente. GZip requiere tener toda la respuesta antes de que se comprima, por lo que no le permitirá transmitir. – Leopd

+1

@ Xealot: Tengo un problema similar con GzipMiddleware. Presentó un error porque ese middleware no admite generadores (en realidad borra el generador accidentalmente): [Django ticket # 15066] (http://code.djangoproject.com/ticket/15066) – AndiDog

33

Gran parte del middleware django evitará la transmisión de contenido. Gran parte de este middleware debe estar habilitado si desea utilizar la aplicación de administración django, por lo que puede ser una molestia. Afortunadamente, esto se resolvió en el django 1.5 release. Puede usar el StreamingHttpResponse para indicar que desea transmitir los resultados de vuelta y todo el middleware que se envía con django es consciente de esto y actúa en consecuencia para no almacenar en búfer el resultado de su contenido, sino que lo envía directamente al final de la línea. Su código se vería así para usar el nuevo objeto StreamingHttpResponse.

def stream_response(request): 
    return StreamingHttpResponse(stream_response_generator()) 

def stream_response_generator(): 
    for x in range(1,11): 
     yield "%s\n" % x # Returns a chunk of the response to the browser 
     time.sleep(1) 

Nota sobre Apache

I probado lo anterior en Apache 2.2 con Ubuntu 13.04. El módulo apache mod_deflate, que estaba habilitado de forma predeterminada en la configuración que probé, almacenará en búfer el contenido que está intentando transmitir hasta que alcance un determinado tamaño de bloque, luego comprimirá gzip el contenido y lo enviará al navegador. Esto evitará que el ejemplo anterior funcione como se desee. Una forma de evitar esto es desactivar mod_deflate poniendo la siguiente línea en su configuración de Apache:

SetEnvIf Request_URI ^/mysite no-gzip=1 

Esto se discute más en la cuestión How to disable mod_deflate in apache2?.

+0

¡Funciona! pero ¿cómo se supone que voy a renderizarlo en la plantilla 'en tiempo real'? Actualmente estoy usando Javascript con una llamada ajax ... pero publica la salida ... solo cuando se termina de procesar ... Por lo tanto, no es 'Streaming' cuando se trata de renderizarlo en el' navegador'. Cualquier ayuda sería apreciada..Gracias :) –

+0

@FirstBlood Con ajax puedes recibir devoluciones de llamada ya que se está transmitiendo y no solo cuando se completa la descarga. Consulte esta respuesta http://stackoverflow.com/a/4488132/1699750 para mostrar el progreso de una llamada Ajax. –

+0

Gracias .. Quiero lograr el estado de salida 'Ping' .. para imprimir en tiempo real en el navegador ... Así que por ejemplo:' ping -c 3 www.google.com' .. cómo el símbolo del sistema da la salida ¿Es posible en 'Django', derramar la salida al navegador cuando leo por' subproceso'? Probé el 'StreamingHttpResponse', pero no parece transmitir en tiempo real .. Incluso traté de configurar' encabezados: Keep-Alive' .. pero no tiene efecto :( –

Cuestiones relacionadas