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); passe
task_name= explicitamente só para sobrescrever.
Intervalo de polling por ModelAdmin¶
class MeuModeloAdmin(CeleryTaskMonitorMixin, admin.ModelAdmin):
celery_poll_interval = 3000 # 3s, em vez do default global
Quando omitido (None, o default), o intervalo efetivo vem de
CELERY_TASK_MONITOR_POLL_INTERVAL (ver Configuração).
get_celery_poll_interval() calcula esse valor e pode ser sobrescrito se a
lógica precisar ser dinâmica (por exemplo, por usuário ou por objeto).
Nome customizado da coluna¶
Use celery_task_field quando 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"]
Internamente, o mixin cria uma cópia do método de renderização com o nome
indicado, evitando que você precise reimplementar a lógica de busca do
TaskLog.
Customizando o template do badge¶
task_status_badge.html é resolvido pelo mecanismo normal de templates do
Django — para customizá-lo, crie um arquivo com o mesmo caminho relativo em
um diretório de templates do seu projeto que tenha prioridade sobre o do
plugin:
seu_projeto/templates/django_celery_task_monitor/task_status_badge.html
O contexto disponível inclui task_log, status, status_display,
poll_url e poll_interval. Mantenha o atributo data-poll-url no
elemento raiz — é ele que task-poll.js usa para descobrir o que sondar.
Customizando o painel de status do change form¶
task_status_panel.html (diferente de task_status_badge.html, que é
um rótulo curto) mostra a frase completa de status. Sobrescreva do mesmo
jeito:
seu_projeto/templates/django_celery_task_monitor/task_status_panel.html
O contexto inclui task_log, status, message (a frase já
composta pelo Python, usada no primeiro render antes do JavaScript
assumir), poll_url e poll_interval. Assim como o badge, mantenha
data-poll-url no elemento raiz. O elemento com a classe
task-status-panel__message é o que task-poll.js atualiza a cada
poll (e a cada segundo, localmente, para o relógio de tempo decorrido —
ver JavaScript).
Endpoint REST fora do admin¶
CeleryTaskMonitorMixin já cria uma rota por ModelAdmin. 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"),
]
Diferente da rota do mixin (que checa has_view_permission do
ModelAdmin), TaskStatusView só exige que o usuário esteja
autenticado (LoginRequiredMixin) — ajuste a permissão sobrescrevendo
get() se precisar de uma checagem mais estrita.
Usando o badge fora do admin¶
{% load task_monitor_tags %}
{% task_status_badge my_task_log %}
{# 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" %}
Veja a lista completa de tags em Referência da API.
Progresso percentual¶
Dentro de uma tarefa com bind=True:
@shared_task(bind=True)
def minha_task(self, ...):
for step, total in enumerate(passos, start=1):
...
self.update_state(state="PROGRESS", meta={"percent": round(step / total * 100)})
TaskLog.get_progress() decodifica esse meta (guardado pelo Celery no
campo result do TaskResult — não há campo próprio de “progresso” no
Celery, então é assim que qualquer estado customizado transporta dados). O
payload JSON do polling inclui progress (o dict decodificado, ou
None) e message já pronta com “Processamento em X%.” anexado quando
progress.percent existe. Isso funciona para o nome de estado
"PROGRESS" ou qualquer outro nome customizado — o único requisito é que
o result codificado seja um JSON decodificável para um dict (o
padrão do Celery, content_type == "application/json").
Mensagens customizadas (“Tarefa enfileirada.”, etc.)¶
Os textos padrão (pt-BR) usados no painel vêm de duas fontes:
No servidor (primeiro render, antes do JS assumir, e no campo
messagedo payload JSON):django_celery_task_monitor.models._compose_status_message, usandogettext— traduzível via o mecanismo normal de i18n do Django (verCONTRIBUTING.mdpara regenerar/compilar traduções).No cliente (recalculado a cada segundo, para o relógio de “há X tempo” andar sem round-trip ao servidor):
DEFAULT_MESSAGESemtask-poll.js, sobrescrevível por chamada viaTaskPoll.init(selector, {messages: {...}})ou globalmente viaTaskPoll.configure({messages: {...}}). Ver JavaScript para a lista completa de chaves.
As duas fontes usam o mesmo texto pt-BR por padrão, mas são independentes — customizar uma não afeta a outra.