Skip to main content

django-celery-task-monitor

CI License: MIT pre-commit

Monitoramento de tarefas Celery no Django Admin, com polling via REST.

Vincule qualquer tarefa Celery disparada a partir do admin a qualquer modelo do seu projeto (via GenericForeignKey), mostre uma coluna de status no changelist que se atualiza sozinha (sem recarregar a página) e controle quem pode ver o stacktrace completo quando uma tarefa falha.

Funcionalidades

  • Modelo TaskLog — vincula uma tarefa Celery a qualquer instância de modelo Django, sem acoplamento (GenericForeignKey).
  • Mixin CeleryTaskMonitorMixin — adiciona uma coluna de status com polling automático a qualquer ModelAdmin.
  • Endpoint REST embutido — cada ModelAdmin que usa o mixin ganha sua própria rota de polling, respeitando as permissões do admin.
  • JavaScript modular e auto-contido (task-poll.js) — sem dependências externas, com limpeza automática de setInterval (sem memory leak) e suporte a múltiplas instâncias na mesma página.
  • TaskLogAdmin — interface central para consultar todas as tarefas registradas, com filtros e paginação.
  • Controle de permissão granular — usuários comuns veem uma mensagem de erro amigável; superusuários e usuários com a permissão view_task_trace veem o stacktrace completo.
  • Internacionalizado — strings traduzidas para pt-BR e en.

Instalação

pip install django-celery-task-monitor

O único pré-requisito é django-celery-results, que fornece o modelo TaskResult usado para consultar o resultado real das tarefas.

Configuração

Adicione as duas apps ao settings.py do seu projeto (django_celery_results é uma dependência direta e precisa estar instalada também):

INSTALLED_APPS = [
    # ...
    "django_celery_results",
    "django_celery_task_monitor",
]

Rode as migrações:

python manage.py migrate

Todas as configurações abaixo são opcionais — o plugin funciona com os valores padrão:

# Intervalo padrão (ms) do polling no changelist, usado quando o ModelAdmin
# não define `celery_poll_interval`. Default: 5000.
CELERY_TASK_MONITOR_POLL_INTERVAL = 5000

# Nome completo da permissão que libera o stacktrace. Default: já é este.
CELERY_TASK_MONITOR_TRACE_PERMISSION = "django_celery_task_monitor.view_task_trace"

# Mensagem exibida a quem não tem permissão de ver o stacktrace.
CELERY_TASK_MONITOR_FRIENDLY_ERROR_MESSAGE = "A tarefa falhou. Fale com o suporte."

# Itens por página no changelist do TaskLogAdmin. Default: 50.
CELERY_TASK_MONITOR_LIST_PER_PAGE = 50

A permissão view_task_trace é criada automaticamente pela migração do plugin. Conceda-a a quem deve ver stacktraces completos (via admin de Usuários/Grupos, ou por script/data migration no seu projeto).

Para o painel de status ao vivo do change form (ver "Uso básico" abaixo) mostrar quando a tarefa realmente começou a rodar — e não ficar preso em "Tarefa enfileirada." — ative também, na configuração do Celery do seu projeto:

CELERY_TASK_TRACK_STARTED = True

Uso básico

from django.contrib import admin
from django.http import HttpResponseRedirect

from django_celery_task_monitor.admin import CeleryTaskMonitorMixin

from .models import MeuModelo
from .tasks import minha_task


@admin.register(MeuModelo)
class MeuModeloAdmin(CeleryTaskMonitorMixin, admin.ModelAdmin):
    list_display = ["nome", "campo1", "task_status_column"]

    def response_change(self, request, obj):
        if "_processar-async" in request.POST:
            # start_task() dispara minha_task.delay(obj.id) e já registra o
            # TaskLog correspondente — sem precisar montar ContentType,
            # object_id, task_id etc. na mão.
            self.start_task(request, obj, minha_task, obj.id)
            self.message_user(request, "Task iniciada!")
            return HttpResponseRedirect(request.path)

        return super().response_change(request, obj)

Isso é tudo: a coluna task_status_column aparece no changelist, mostra o badge de status da tarefa mais recente vinculada a cada linha, e começa a sondar o endpoint REST automaticamente assim que a página carrega — sem nenhum JavaScript extra para escrever. Para o botão "Processar (assíncrono)" aparecer no formulário, adicione um submit_buttons_bottom customizado (veja example/example_app/templates/admin/example_app/relatorio/change_form.html no projeto de exemplo).

Importante: o changelist ganha o badge automaticamente, mas o change form (a página para onde response_change redireciona) não ganha nenhum indicador ao vivo sozinho — só a mensagem estática de self.message_user(...), que nunca muda depois de renderizada. Se você quiser um painel que também evolua em tempo real ali ("Tarefa enfileirada." → "Tarefa em processamento há 12s." → "Tarefa finalizada com sucesso."), referencie {{ task_log_panel_html }} no seu change_form.html — o mixin já injeta esse HTML pronto no contexto:

{% extends "admin/change_form.html" %}

{% block field_sets %}
  {{ task_log_panel_html }}
  {{ block.super }}
{% endblock %}

Uso avançado

Disparando tarefas em outros lugares (actions, etc.)

start_task()/create_task_log() não são exclusivos de response_change — funcionam em qualquer lugar do ModelAdmin, inclusive numa action de changelist (uma tarefa por objeto selecionado):

class MeuModeloAdmin(CeleryTaskMonitorMixin, admin.ModelAdmin):
    actions = ["processar_selecionados_async"]

    @admin.action(description="Processar selecionados (assíncrono)")
    def processar_selecionados_async(self, request, queryset):
        for obj in queryset:
            self.start_task(request, obj, minha_task, obj.id)
        self.message_user(request, f"{queryset.count()} tarefa(s) iniciada(s)!")

Se a tarefa já foi disparada de outro jeito (apply_async() com opções customizadas, por exemplo) e você só tem o task_id em mãos, use create_task_log() diretamente — é o que start_task() usa por baixo:

result = minha_task.apply_async((obj.id,), countdown=60)
self.create_task_log(request, obj, result.id, task_name="minha_task")

task_name é opcional em start_task() — é derivado automaticamente de task.name (todo @shared_task/@app.task tem esse atributo).

Customizar o intervalo de polling por ModelAdmin

class MeuModeloAdmin(CeleryTaskMonitorMixin, admin.ModelAdmin):
    celery_poll_interval = 3000  # 3s, em vez do default global

Customizar o nome da coluna/atributo

Use celery_task_field quando o nome padrão (task_status_column) colidir com outro atributo já existente na sua ModelAdmin:

class MeuModeloAdmin(CeleryTaskMonitorMixin, admin.ModelAdmin):
    celery_task_field = "status_da_tarefa"
    list_display = ["nome", "status_da_tarefa"]

Usar o badge fora do admin

Template tags ({% load task_monitor_tags %}):

{% load task_monitor_tags %}

{% task_status_badge my_task_log %}

{# ou, com URL/intervalo de polling customizados: #}
{% task_status_badge my_task_log poll_url=my_poll_url poll_interval=3000 %}

{# inclui o <script> do plugin com a inicialização já feita: #}
{% task_poll_script ".task-status-badge" %}

Um endpoint REST único, fora do admin

CeleryTaskMonitorMixin já cria uma rota por ModelAdmin (admin:<app>_<model>_celery_task_status). Se preferir um único endpoint compartilhado fora do admin, use a view genérica:

# urls.py
from django_celery_task_monitor.views import TaskStatusView

urlpatterns = [
    path("task-status/<str:task_id>/", TaskStatusView.as_view(), name="task-status"),
]

JavaScript: uso manual

task-poll.js se auto-inicializa em qualquer elemento com data-poll-url (exatamente o que o template task_status_badge.html renderiza), então na maioria dos casos você não precisa chamar nada manualmente. Para controle fino (callbacks, seletor customizado):

<script src="{% static 'django_celery_task_monitor/js/task-poll.js' %}"></script>
<script>
  TaskPoll.init(".task-badge", {
    pollInterval: 5000,
    endpoint: "/admin/task-status/",  // opcional se o elemento já tem data-poll-url
    onUpdate: function (data, el) { /* a cada resposta */ },
    onSuccess: function (data, el) { console.log("Task concluída!", data); },
    onError: function (data, el) { console.warn("Task falhou", data); },
  });
</script>

TaskPoll.stop(el) para um elemento específico, TaskPoll.stopAll() para todos. Intervals são limpos automaticamente quando o elemento é removido do DOM (via MutationObserver) e quando a tarefa atinge um estado final (SUCCESS, FAILURE ou REVOKED).

Elementos com um .task-status-panel__message interno (renderizados por task_status_panel.html) ganham a frase completa de status, recomposta a cada poll e a cada segundo no cliente (relógio de "há X tempo"). Os textos padrão (pt-BR) são customizáveis via TaskPoll.configure({messages: {...}}) ou por chamada via options.messages — veja docs/javascript.rst.

Referência da API

django_celery_task_monitor.models.TaskLog

Campo/Método Descrição
content_type, object_id, content_object Vínculo genérico com qualquer modelo.
task_id ID da tarefa Celery (único).
task_name Nome da tarefa (informativo).
status Status cacheado (PENDING, STARTED, RETRY, PROGRESS, SUCCESS, FAILURE, REVOKED, ou qualquer estado customizado).
started_by Usuário que disparou a tarefa (opcional).
update_status() Sincroniza status com o TaskResult mais recente.
is_finished True se o status é terminal.
get_progress() Dict de progresso ({"percent": ...}) publicado via self.update_state(state=..., meta=...), ou None.
get_status_message() Frase de status legível (ex.: "Tarefa em processamento há 12s.").
get_error_details(user) {"message": ..., "traceback": ...}, respeitando a permissão de user.
get_traceback() Stacktrace bruto, sem checar permissão (uso interno).
as_status_payload(user) Payload JSON usado pelo endpoint de polling (inclui message, started_at, progress).

django_celery_task_monitor.admin.CeleryTaskMonitorMixin

Atributo/Método Descrição
celery_poll_interval Intervalo de polling (ms) desta ModelAdmin. None usa o default global.
celery_task_field Nome do atributo/coluna de status. Default: "task_status_column".
task_status_column(obj) Método de exibição padrão para list_display.
get_celery_poll_interval() Intervalo efetivo (considerando o default global).
get_urls() Registra a rota REST de polling (admin:<app>_<model>_celery_task_status).
render_change_form(...) Injeta task_log_panel_html (painel ao vivo pronto) no contexto do change form.
start_task(request, obj, task, *args, task_name=None, **kwargs) Dispara task.delay(*args, **kwargs) e já registra o TaskLog. Funciona em response_change, actions de changelist, ou qualquer lugar do ModelAdmin.
create_task_log(request, obj, task_id, task_name="") Registra o TaskLog de uma tarefa já disparada de outro jeito (apply_async(), etc.). É o que start_task() usa por baixo.

django_celery_task_monitor.admin.TaskLogAdmin

Admin somente leitura (sem criação manual) registrado para TaskLog, com filtros por status/task_name/content_type, busca por task_id e task_name, e ocultação automática do campo de stacktrace completo para quem não tem a permissão view_task_trace.

Template tags ({% load task_monitor_tags %})

Tag Descrição
{% task_status_badge task_log %} Renderiza o badge de status (rótulo curto).
{% task_status_panel task_log %} Renderiza o painel de status ao vivo (frase completa).
{% task_poll_script selector %} <script> do plugin + inicialização do polling para selector.
{% task_monitor_static_url %} URL estática de task-poll.js.
{% task_monitor_static_css_url %} URL estática do CSS opcional do badge.

django_celery_task_monitor.permissions.user_can_view_task_trace(user)

Retorna True para superusuários e usuários com a permissão view_task_trace; False para usuários anônimos/sem permissão.

Exemplo completo

Veja example/ — um projeto Django mínimo com um ModelAdmin usando CeleryTaskMonitorMixin exatamente como mostrado acima, incluindo o botão "Processar (assíncrono)" no formulário de edição. Para rodar:

git clone https://github.com/django-by-kelsoncm/django-celery-task-monitor.git
cd django-celery-task-monitor
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
cd example
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver

O exemplo já roda com CELERY_TASK_ALWAYS_EAGER = True (sem precisar de worker/broker) — veja example/example_project/settings.py.

FAQ

Como mostrar uma barra de progresso, em vez de só um badge de status?

O plugin já lê progresso percentual nativamente: dentro da sua tarefa Celery (com bind=True), chame self.update_state(state="PROGRESS", meta={"percent": 42}). TaskLog.get_progress() decodifica isso, e tanto o payload JSON do endpoint de polling (progress.percent) quanto a frase pronta do painel ao vivo ("Processamento em 42%.") já refletem automaticamente — sem customização necessária para o texto. Para uma barra visual em vez de só texto, use o callback onUpdate de TaskPoll.init() para ler data.progress.percent a cada poll e atualizar sua própria UI.

Como integrar com Django-RQ (ou outra fila) em vez de Celery?

O plugin depende de django-celery-results porque é isso que popula TaskResult.status. Para outro backend de filas, você precisaria de um adaptador equivalente que exponha algo parecido com TaskResult (com task_id, status, result, traceback) — hoje isso está fora do escopo do plugin. Uma alternativa mais simples: atualize TaskLog.status diretamente a partir da sua própria fila (chamando task_log.status = ...; task_log.save()), sem depender de update_status()/TaskResult.

Como reprocessar uma tarefa que falhou?

Isso é responsabilidade do seu ModelAdmin (o mesmo padrão do exemplo em "Uso básico" acima), não do plugin — chame minha_task.delay(...) de novo e crie um novo TaskLog. O TaskLogAdmin mostra o histórico completo de tentativas, já que cada TaskLog é imutável quanto a task_id.

Os badges antigos continuam fazendo polling para sempre?

Não. O JavaScript para o setInterval automaticamente assim que a tarefa atinge um status final (SUCCESS, FAILURE ou REVOKED) — veja TaskPoll em task-poll.js.

Compatibilidade

  • Django 4.2+
  • Celery 5.x
  • Python 3.10+
  • django-celery-results >= 2.5

Desenvolvimento

python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pre-commit install
pre-commit install --hook-type pre-push
pytest
black django_celery_task_monitor tests example
flake8
mypy django_celery_task_monitor

Os hooks de pre-commit (.pre-commit-config.yaml) rodam automaticamente: black/flake8 a cada commit; mypy/pytest a cada push.

Veja CONTRIBUTING.md para mais detalhes.

Licença

MIT

Metadata

Release files for django-celery-task-monitor 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-celery-task-monitor 0.1.0
File Size Uploaded Provenance
django_celery_task_monitor-0.1.0.tar.gz 35.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for django-celery-task-monitor 0.1.0
File Interpreter ABI Platform
django_celery_task_monitor-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 72.8 kB

Release files / django_celery_task_monitor-0.1.0.tar.gz

Download URL django_celery_task_monitor-0.1.0.tar.gz
Size 35.5 kB
Tags Source
SHA-256 checksum
How to use checksums
40bbe3a2c48abd151fffb1dc17b5e9bde611e3bcdfd6871992d8c38af4f84727
BLAKE2b-256 checksum
How to use checksums
e4552920ad6488476ac483ccccb8474d8dbc1b21e2ef4e41b9db5484aa9629f2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.

Transparency log

Release files / django_celery_task_monitor-0.1.0-py3-none-any.whl

Download URL django_celery_task_monitor-0.1.0-py3-none-any.whl
Size 37.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cc78b6a3830b8f5a0bf20c82c4fb918234db8ab4bdfade89760eee28eebe4150
BLAKE2b-256 checksum
How to use checksums
7c5152dab05aaeaa7811a68ce0deff97a44d9c894b95aed86129c33d747ae012
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.

Transparency log

Security for django-celery-task-monitor 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for django_celery_task_monitor-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page