Celery + Django tutorial (Part 2). 🍮 Results pudding

Here is part two. Let’s make an interface to see what’s going with Celery on right now.

Previous part: Part 1 🥩 Django steak medium rare

Next part: Part 3 🥄 Testing the taste

Following stack: Django + Celery + Nginx

Project structure

  • proj
    • proj
      • __init__.py
      • settings.py
      • urls.py
      • ...
      • celery.py
    • firstapp
      • tasks.py

Content table

🍜 Cooking project dinner

  1. 🥩 Django steak medium rare
    1. 🥗 Broker salad
      1. 🥬 Chopped Redis
      2. 🥕 Carrot for Rabbitmq
    2. 🥒 Pickled tasks
  2. 🍮 Results pudding
    1. 🍪 HTML
    2. 🍯 View
    3. 🍫 Url
  3. 🥄 Testing the taste
  4. 🍻 Serving Celery workers
  5. 🍱 Pack Linux with Celery Daemon

🍜 Cooking project dinner

🍮 2. Results pudding

The django-celery-results package mentioned in Celery Docs does not work with Redis, so I will make a view with my own table of tasks.

2.1 HTML

I am using the Django admin base located at admin/ so my results/redis.html has the following content (I also took code for tabs from W3School.com and modified it to fit my needs):

There are CSS for table and tabs, JS for tabs and HTML for a table. If there is something in the tab to show, it is green, otherwise grey and disabled. I tested it just a bit, I will return to it later, at the moment I don’t really need it as much.

But you, folks, go ahead and use it to build your own cool interface, if you want to.

It looks like this:

Это изображение имеет пустой атрибут alt; его имя файла - screenshot-from-2019-10-08-16-26-52.png
{% extends 'admin/base.html' %}

{% block content_title %}{{ title }}{% endblock content_title %}

{% block content %}
    <br>{% for x in test %}{{ x }}<br>{% endfor %}
    <style>/* Style the tab */
    .tab {
        overflow: hidden;
        border: 1px solid #ccc;
        background-color: #f1f1f1;
    }

    .tab button {
        background-color: inherit;
        float: left;
        border: none;
        outline: none;
        cursor: pointer;
        padding: 14px 16px;
        transition: 0.3s;
    }

    .tab button.active {
        background-color: #ccc;
    }

    .tabcontent {
        display: none;
        padding: 6px 12px;
        border: 1px solid #ccc;
        border-top: none;
    }

    th {
        font-size: 20px;
        font-weight: 400;
        padding: 7px 15px;
        border-right: 1px black solid;
        line-height: 20px;
    }
    </style>
    <script>function openCity(evt, cityName) {
        // Declare all variables
        var i, tabcontent, tablinks;
        // Get all elements with class="tabcontent" and hide them
        tabcontent = document.getElementsByClassName("tabcontent");
        for (i = 0; i < tabcontent.length; i++) {
            tabcontent[i].style.display = "none";
        }
        // Get all elements with class="tablinks" and remove the class "active"
        tablinks = document.getElementsByClassName("tablinks");
        for (i = 0; i < tablinks.length; i++) {
            tablinks[i].className = tablinks[i].className.replace(" active", "");
        }
        // Show the current tab, and add an "active" class to the button that opened the tab
        document.getElementById(cityName).style.display = "block";
        evt.currentTarget.className += " active";
    }</script>
    <div class="tab" style="margin-top: 1%;">
        <button class="tablinks" style="{% if results %}background: lightgreen;"{% else %}cursor:
                default;" disabled{% endif %}
        onclick="openCity(event, 'results')">Results
        </button>
        <button class="tablinks" style="{% if active_queues %}background: lightgreen;"{% else %}cursor:
                default;" disabled{% endif %}
        onclick="openCity(event, 'active_queues')">Active queues
        </button>
        <button class="tablinks" style="{% if active_tasks %}background: lightgreen;"{% else %}cursor:
                default;" disabled{% endif %}
        onclick="openCity(event, 'active_tasks')">Active tasks
        </button>
        <button class="tablinks" style="{% if reserved_tasks %}background: lightgreen;"{% else %}cursor:
                default;" disabled{% endif %}
        onclick="openCity(event, 'reserved_tasks')">Reserved tasks
        </button>
        <button class="tablinks" style="{% if scheduled_tasks %}background: lightgreen;"{% else %}cursor:
                default;" disabled{% endif %}
        onclick="openCity(event, 'scheduled_tasks')">Scheduled tasks
        </button>
    </div>

    <!-- Tab content --><br>
    <div id="results" class="tabcontent">
        <h3>Results</h3>
        {% if test %}
            <table>
                <tr style="border-bottom: 2px solid black;">
                    <th>Arguments</th>
                    <th>Results</th>
                </tr>
                {% for x in test %}
                    {% for y in x %}
                        <tr>
                            <th>{{ y }}</th>
                            <th></th>
                        </tr>
                    {% endfor %}
                {% endfor %}
            </table>
        {% else %}No results{% endif %}
    </div>

    <div id="active_queues" class="tabcontent">
        <h3>Active queues</h3>
        {% if active_queues %}
{#            {{ active_queues }}#}
            <table>{% for x in active_queues %}

                <tr>
                    <th>{{ key }}</th>
                    <th>{{ value }}</th>
                </tr>{% endfor %}</table>
        {% else %}No results{% endif %}
    </div>

    <div id="active_tasks" class="tabcontent">
        <h3>Active tasks</h3>
        {% if active_tasks %}
            {% for x in active_tasks %}
                {% for key, value in x %}
                    {{ key }}: {{ value }}<br>
                {% endfor %}
                <br>
            {% endfor %}
        {% else %}<p>No active tasks</p>{% endif %}
    </div>

    <div id="reserved_tasks" class="tabcontent">
        <h3>Reserved tasks</h3>
        {% if reserved_tasks %}
            {% for x in active_tasks %}
                {% for key, value in x %}
                    {{ key }}: {{ value }}<br>
                {% endfor %}
                <br>
            {% endfor %}
        {% else %}<p>No active tasks</p>{% endif %}
    </div>

    <div id="scheduled_tasks" class="tabcontent">
        <h3>Scheduled tasks</h3>
        {% if scheduled_tasks %}
            {% for x in active_tasks %}
                {% for key, value in x %}
                    {{ key }}: {{ value }}<br>
                {% endfor %}
                <br>
            {% endfor %}
        {% else %}<p>No active tasks</p>{% endif %}
    </div>

{% endblock content %}

2.2 View

The view for the results can be located anywhere.

Default method is:


from django.views.generic.base import TemplateView
from firstapp.tasks import app
from celery.result import AsyncResult
import json, redis

class RedisResults(TemplateView):
    template_name = 'results/redis.html'

    def get_context_data(self, **kwargs):
        context = super(RedisResults, self).get_context_data(**kwargs)
        context['title'] = 'Redis Results'
        r = redis.Redis("localhost", 6379)
        t = r.keys(pattern="celery-task-meta*")
        test = []
        for x in t:
            basic = AsyncResult(json.loads(r.get(x).decode('utf-8'))['task_id'], app=app).get()
            if type(basic) != dict:
                res = {'results': basic[1]}
                basic = basic[0].update(res)
            print(basic)
        context['results'] = test

        i = app.control.inspect()
        context['bonus'] = i

        try: context['active_queues'] = list(i.active_queues().values())[0][0].items()
        except: pass
        try: context['active_tasks'] = list(i.active().values())[0][0].items()
        except: pass

        try: context['reserved_tasks'] = list(i.reserved().values())[0][0].items()
        except: pass

        try: context['scheduled_tasks'] = list(i.scheduled().values())[0][0].items()
        except: pass

        return context

2.3 Url

To urlpatterns list add a new line:

path('results/redis/', celery_views.RedisResults.as_view(), name='results-redis')

Next part: Part 3 🥄 Testing the taste

2 thoughts on “Celery + Django tutorial (Part 2). 🍮 Results pudding

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.