多个芹菜进度条

时间:2017-10-10 15:48:04

标签: python ajax django twitter-bootstrap django-celery

问题

我有一个使用Celery + RabbitMQ的Django站点作为长时间运行任务的任务队列。我将结果存储在Redis中。我已经能够使用Celery的update_state在引导程序进度条中显示一个任务的进度,并通过按钮向Redis DB执行ajax发布以检索当前状态。

理想情况下,我想展示当前正在运行或最近完成的每个任务,这些任务都在Redis中,并且有自己的进度条。目前,我只能通过我的小Click Here按钮显示当前任务的进度。

我曾尝试为进度条创建多个类,但说实话,我对如何执行此操作感到迷茫,似乎无法找到有关如何执行此类操作的任何内容。我试图尽可能多地上传我的代码。任何帮助将不胜感激!

代码

urls.py

urlpatterns = [
    url(r'^poll_state$', poll_state, name="poll_state"),
    url(r'^do_task$', do_task, name="do_task"),
]

views.py

from django.shortcuts import render
import json
from celery.result import AsyncResult
from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.views.generic.base import View
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt 
def do_task(request):
    data = 'Fail'
    if request.is_ajax():
        job = add.delay()
        request.session['task_id'] = job.id
        data = job.id
    else:
        data = 'This is not an ajax request!'
    json_data = json.dumps(data)
    return HttpResponse(json_data, content_type='application/json')


@csrf_exempt
def poll_state(request):
    """ A view to report the progress to the user """
    data = 'Fail'
    if request.is_ajax():
        if 'task_id' in request.POST.keys() and request.POST['task_id']:
            task_id = request.POST['task_id']
            task = AsyncResult(task_id)
            data = task.info
        else:
            data = 'No task_id in the request'
    else:
        data = 'This is not an ajax request'

    json_data = json.dumps(data)

    return HttpResponse(json_data, content_type='application/json')

tasks.py

from __future__ import absolute_import, unicode_literals
from celery import shared_task
from celery.decorators import task
from celery import current_task
from celery.result import AsyncResult
import celery
from .celery import app
import time

#Task loops every half second to update the current state
@task(bind=True, ignore_result=True)
def add(self):
    for i in range(101):
        time.sleep(0.5)
        self.update_state(state="PROGRESS", meta={'current': i, 'total': 100})

celery.py

from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from django.conf import settings


# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yodaclaw.settings')

app = Celery('myAppName')

# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
#   should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')

# Load task modules from all registered Django app configs.
# This allows you in shell to not have to import yodaclaw.tasks
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

@app.task
def debug_task(self):
    print('Request: {0!r}'.format(self.request))

settings.py

# Celery Settings

CELERY_BROKER_URL = 'amqp://localhost'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_TRACK_STARTED = True

base.py

{% load static %}
<!DOCTYPE html>
<html>
<head>
    {% block title_outer %}
    {% endblock %}

    {% block meta %}
        <meta charset="utf-8">
        <meta http-equiv="X-UA-COMPATIBLE" content="IE=edge">
        <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
    {% endblock %}

    {% block stylesheets %}
    {% endblock %}

    {% block javascript %}
    {% endblock %}

    {% block extra_head %}
    {% endblock %}
</head>

<body>
{% block body %}
    <div class="wrapper">
        {% block nav_header %}
        {% endblock %}

        {% block nav_sidebar %}
        {% endblock %}

        {% block content_wrapper %}
        <div class="content-wrapper">
            {% block content_header %}
                <section class="content-header"> 
                </section>
            {% endblock %}

            {% block content_outer %}
            <section class="content">
                {% block messages %}
                {% endblock %}

                {% block content_block_wrap %}
                    {% block content %}{% endblock %}
                {% endblock %}
            </section>
            {% endblock %}
        {% endblock content_wrapper %}

        {% block nav_footer %}
        {% endblock %}
    </div>

<!-- The Right Sidebar -->
<aside class="control-sidebar control-sidebar-light">
  <!-- Content of the sidebar goes here -->
{% if task_id %}
    <h6>Task ID: {{ task_id }}</h6>
         <div class="progress">
            <div class="progress-bar progress-bar-success progress-bar-striped" role="progressbar"
                aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
                {% if task_id %} {{ task_id }} {% endif %}
            </div>
        </div>
{% endif %}
    <div id="container">
        <div id="action">
            <button id="do-task">Click here!</button>
        </div>
    </div>

<!-- Ajax Post to poll_state -->
    {% if task_id %}
    <script type="text/javascript">
    jQuery(document).ready(function() {
        var PollState = function(task_id) {
            jQuery.ajax({
                url: "poll_state",
                type: "POST",
                data: "task_id=" + task_id,
            }).done(function(task) {
                if (task.current) {
                    jQuery('.progress-bar').css({'width': task.current + '%'});
                    jQuery('.progress-bar').html(task.current + '%');
                }
                else {
                    jQuery('.status').html(task);
                };
                PollState(task_id);
            });
        }
        PollState('{{ task_id }}');
    })
    </script>
    {% endif %}

 <!-- Clickable button for do_task -->
    <script type="text/javascript">
        jQuery('#do-task').click( function() {
            jQuery.ajax({
                url: "do_task",
                data: {},
                success: function(){
                    jQuery.ajax({
                        url: "",
                        context: document.body,
                        success: function(s, x) {
                            jQuery(this).html(s);
                        }
                    });
                }
            })
        });
    </script>
</aside>
<!-- The sidebar's background -->
<!-- This div must placed right after the sidebar for it to work-->
<div class="control-sidebar-bg"></div>
{% endblock body %}



</body>
{% block extra_foot %}{% endblock %}
</html>

1 个答案:

答案 0 :(得分:0)

签出celery-progress,应该可以处理这个问题。它应该简单到将不同的任务ID放入前端并将每个ID与不同的div相关联的程度。例如。像这样的东西:

var progressUrl1 = "{% url 'celery_progress:task_status' task_id1 %}";
var progressUrl2 = "{% url 'celery_progress:task_status' task_id2 %}";
document.addEventListener("DOMContentLoaded", function () {
  CeleryProgressBar.initProgressBar(progressUrl1);
  CeleryProgressBar.initProgressBar(progressUrl2);
});

demo page linked from the readme上有许多不同的进度条。