django-celery-task-monitor
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 qualquerModelAdmin. - Endpoint REST embutido — cada
ModelAdminque 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 desetInterval(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_traceveem o stacktrace completo. - Internacionalizado — strings traduzidas para
pt-BReen.
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
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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| django_celery_task_monitor-0.1.0.tar.gz | 35.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.