Necesito una página web HTML en mi aplicación Django para cargar y mostrar la salida continua de un script en un cuadro desplazable. es posible?
Actualmente estoy usando un subproceso para ejecutar una secuencia de comandos de Python, pero la página HTML no se cargará hasta que finalice la secuencia de comandos (lo que puede demorar unos 5 minutos). Quiero que los usuarios vean que algo está sucediendo, en lugar de solo un círculo giratorio.
Lo que ya tengo también descarga la salida completa del script con "\n" en el texto; En su lugar, me gustaría generar cada nueva línea, si es posible.
Mi código es el siguiente:
Vistas.py:
def projectprogress(request): GenerateProjectConfig(request) home = os.getcwd() project_id = request.session['projectname'] staging_folder = home + "/staging/" + project_id + "/" output = "" os.chdir(staging_folder) script = home + '/webscripts/terraformdeploy.py' try: output = subprocess.check_output(['python', script], shell=True) except subprocess.CalledProcessError: exit_code, error_msg = output.returncode, output.output os.chdir(home) return render(request, 'projectprogress.html', locals())
progreso del proyecto.html:
<style> div.ex1 { background-color: black; width: 900px; height: 500px; overflow: scroll; margin: 50px; } </style> <body style="background-color: #565c60; font-family: Georgia, 'Times New Roman', Times, serif; color: white; margin:0"></body> <div class="ex1"> {% if output %}<h3>{{ output }}</h3>{% endif %} {% if exit_code %}<h3> The command returned an error: {{ error_msg }}</h3>{% endif %} </div> <div class="container"> <a class="button button--wide button--white" href="home.html" title="Home" style="color: white; margin: 60px;"> <span class="button__inner"> Home </span> </a> </div> </body> </html>
Podría simplificar su tarea usando StreamingHttpResponse y Popen :
def test_iterator(): from subprocess import Popen, PIPE, CalledProcessError with Popen(['ping', 'localhost'], stdout=PIPE, bufsize=1, universal_newlines=True) as p: for line in p.stdout: yield(line + '<br>') # process line here if p.returncode != 0: raise CalledProcessError(p.returncode, p.args) def busy_view(request): from django.http import StreamingHttpResponse return StreamingHttpResponse(test_iterator())
StreamingHttpResponse
espera un iterador como parámetro. Una función iteradora es aquella que tiene una expresión de yield
(o una expresión generadora) y su valor de retorno es un objeto generador (un iterador).
En este ejemplo, simplemente hago eco del comando ping para demostrar que funciona.
Sustituya ['ping', 'localhost']
por una lista (tiene que ser una lista si pasa parámetros al comando, en este caso, localhost
). Su ['python', script]
original debería funcionar.
Si quieres saber más sobre generadores, te recomiendo la charla de Trey Hunner, y también que leas el capítulo 14 del libro Fluent Python . Ambos son fuentes asombrosas.
Descargo de responsabilidad:
Consideraciones de rendimiento
Django está diseñado para solicitudes de corta duración. La transmisión de respuestas vinculará un proceso de trabajo durante toda la duración de la respuesta. Esto puede resultar en un bajo rendimiento.
En términos generales, debe realizar tareas costosas fuera del ciclo de solicitud-respuesta, en lugar de recurrir a una respuesta transmitida.