Django Redis Tasks
This is a Redis-backed implementation of Django's django.tasks
framework.
django-queues
Queue storage and worker execution are provided by django-queues, a
generic Django queue package. django-redis-tasks is a thin translation layer on top of it: it maps Django's task
vocabulary (Task, TaskResult, TaskResultStatus) onto django-queues' generic queue vocabulary (QueueEntry,
QueueEntryStatus), and provides the handler function that executes queued task calls.
This package does not manage Redis connections, run a worker loop, or start anything during Django's application
startup — django-queues owns all of that, including its manage.py runqueues management command.
Deferred tasks
run_after is supported. The backend normalizes the datetime to UTC at enqueue: that UTC instant is stored on the task payload and passed to django-queues as available_at. Reconstruction therefore does not depend on the reader's USE_TZ or TIME_ZONE. Redis TIME decides when the entry becomes claimable; workers do not record an attempt or emit started/finished signals while the task is only waiting.
- Aware datetimes are converted to UTC.
- Naive datetimes are accepted only when
USE_TZisFalse; they are interpreted in Django's current timezone, then converted to UTC. WhenUSE_TZisTrue, Django rejects a naiverun_afterat enqueue. Legacy naive payload values are still normalized to UTC on read. - A past or current
run_afteris eligible for dispatch immediately. - A worker that has no due work waits until a deferred task becomes due; it does not busy-poll the application clock.
Redis runtime
django-queues 1.2.0 requires Redis 7+ and a deployed Function library before the application or a worker starts:
python manage.py redis_lua_lib --deploy
Queue aliases must match [A-Za-z0-9_-]. Redis keys from django-queues before 1.2.0 are not compatible; start from empty queue keys or a new Redis logical database.
Installation
uv add django-redis-tasks
Configuration
django_queue must be added to INSTALLED_APPS. redis_tasks does not require
app registration today, but adding it is recommended for forward compatibility
with future Django integration points such as models, observer registrations,
templates, and static assets:
INSTALLED_APPS = [
...
"django_queue",
"redis_tasks",
...
]
Configuration is two settings working together: QUEUES (owned by django-queues) configures the underlying Redis
queue and registers this package's handler; TASKS (owned by Django) configures the task backend and points it at
that queue.
QUEUES = {
"default": {
"BACKEND": "django_queue.backends.redis.RedisAsyncPriorityQueueJson",
"LOCATION": "redis://localhost:6379/12",
"HANDLER": "redis_tasks.handlers.handle_task_entry",
"ENTRY_CLASS": "redis_tasks.entries.TaskQueueEntry",
"WORKER": "redis_tasks.worker.RedisTaskWorker",
},
}
TASKS = {
"default": {
"BACKEND": "redis_tasks.backend.RedisBackend",
"OPTIONS": {
"queue_alias": "default", # the QUEUES alias to delegate to; defaults to this TASKS alias
},
},
}
A Django project may configure multiple QUEUES/TASKS alias pairs, each backed by its own Redis queue.
TaskQueueEntry records worker_ids as the ordered history of worker-process UUIDs that began dispatching a task;
TaskResult.attempts is the length of that list. Redis automatically recovers an entry whose worker lease expires, so a
task can execute more than once. Make handlers with side effects idempotent.
Running a worker
Task execution happens out-of-process, via django-queues' own management command:
python manage.py runqueues
This discovers every QUEUES alias with a HANDLER configured (including redis_tasks.handlers.handle_task_entry)
and runs its worker until the process receives a termination signal. Queue connections are initialized lazily when
their aliases are first used, but no task worker or handler execution starts in a Django web process — run runqueues
as a separate, standalone service.
Async applications
django-redis-tasks supports Django's native asynchronous task API. In an
ASGI view, consumer, or other coroutine, use await task.aenqueue(...) to
write directly through django-queues' asynchronous Redis API. Refresh an
existing result with await result.arefresh(). Neither operation needs this
backend to cross Django's synchronous task-backend bridge, so queue I/O stays
on the caller's event loop.
from django.http import JsonResponse
async def start_report(request):
result = await build_report.aenqueue(request.user.pk)
return JsonResponse({"task_id": result.id})
async def refresh_report(result):
await result.arefresh()
return result.status
Tasks declared with async def are also awaited directly by this package's
handler inside django-queues' worker event loop. A synchronous task function
is still safe, but Django runs it through a thread bridge to avoid blocking
that loop.
Queue work is deliberately outside the ASGI request runtime. The separate
runqueues service owns task dispatch and recovery, while django-queues'
process-local queue runtime owns lifecycle-observer delivery. This lets an
application use task status and best-effort queue observers without starting
workers from views or competing with request handling for an event loop.
An entirely asynchronous application should also use async middleware, views, task functions, and task-signal receivers. Synchronous middleware, ORM work, task functions, and signal receivers remain supported, but Django adapts those specific boundaries through its thread bridge.
Usage
A task is created using Django's @task decorator:
from django.tasks import task
@task()
def calculate_meaning_of_life() -> int:
return 42
@task()
async def nudge_nudge_wink_wink() -> list[str]:
return ["say", "no", "more"]
Release files for django-redis-tasks 1.0.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 | |
|---|---|---|---|
| django_redis_tasks-1.0.0.tar.gz | 15.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_redis_tasks-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.4 kB
Release files / django_redis_tasks-1.0.0.tar.gz
| Download URL | django_redis_tasks-1.0.0.tar.gz |
|---|---|
| Size | 15.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d5f88a286f30139ff145c2c31d1af4307443b424dbd4d5c683cf1c72a291158b
|
|
BLAKE2b-256 checksum How to use checksums |
25f9a7a8a00a01051a57dd4bff5fdc1bd6c987079b0f76d6a53f18d0f6b9fd52
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / django_redis_tasks-1.0.0-py3-none-any.whl
| Download URL | django_redis_tasks-1.0.0-py3-none-any.whl |
|---|---|
| Size | 10.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3e472cccfd628401be261fc2e1fffa1f7846a426a372f42583251ba89dc2fdef
|
|
BLAKE2b-256 checksum How to use checksums |
bd4e61bee090ab0117a216635daa9af4aaed695dcf26c1008c1e4143abff763f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|