Use Procrastinate in a Django application¶
Defining tasks¶
Add your tasks to tasks.py in your Django app.
Inside tasks, you can use the classical ORM API or the async ORM API to access your models.
from procrastinate.contrib.django import app
@app.task
def mytask1(obj_pk):
obj = MyModel.objects.get(pj=obj_pk)
...
@app.task
async def mytask2(obj_pk):
obj = await MyModel.objects.aget(pj=obj_pk)
...
See Define a task for more details on how to define tasks.
Running the worker & other CLI commands¶
Run the worker with the following command.
(venv) $ ./manage.py procrastinate worker
./manage.py procrastinate mostly behaves like the procrastinate command
itself. The subcommand schema is not available, as Procrastinate will use
Django migrations. The --app option is also not available, as Procrastinate will use
the automatically provided app (see Configure Django & Procrastinate to work together).
Note
As a fully async connector is needed to run the worker, Procrastinate generates
one for you using Psycopg (by default) or Aiopg depending on
whether psycopg version 3 or aiopg is installed, and connects using the
DATABASES settings. If neither library is installed, an error will be raised.
Note
Contrary to the standalone procrastinate CLI, ./manage.py procrastinate
does not support changing the verbosity with -v. -v is a django-controlled
argument and will not be used by Procrastinate. Define your logging configuration
in Django’s settings (see Get Procrastinate logs).
See Use the command line for more details on the CLI. If you prefer writing your own scripts, see Running custom Django scripts.
Database connections¶
The worker manages Django’s database connections around each task the same way
Django does around an HTTP request: per-thread connections are closed before and
after every task (sync or async) via close_old_connections(), and Django’s
query log (only populated under DEBUG=True) is cleared with reset_queries().
So for an ordinary task you don’t have to do anything — the connection it opens is
cleaned up at the task boundaries, and CONN_MAX_AGE is respected, so persistent
connections are reused between tasks rather than force-closed.
The one case this doesn’t cover is within a single long-running task: the worker only closes at task boundaries, so a connection opened early stays open until the task finishes, even across a long stretch of non-database work. That holds a connection slot for the whole task and risks the idle connection being dropped before a later query. If that matters, close it from within the task once you’re done with the database for a while:
from django.db import close_old_connections
@app.task
def long_task():
do_early_db_work()
close_old_connections() # release the connection before the long idle stretch
do_hours_of_non_db_work()
do_late_db_work() # reconnects fresh
Deferring jobs¶
Defer jobs from your views works as you would expect:
from myapp.tasks import mytask
def myview(request):
...
mytask.defer(obj_pk=obj.pk)
async def myasyncview(request):
...
await mytask.defer_async(obj_pk=obj.pk)
See Defer a job for more details on how to defer jobs.
Checking proper configuration¶
You can check that Procrastinate is properly configured by running the following command:
(venv) $ ./manage.py procrastinate healthchecks
Database connection: OK
Migrations: OK
Default Django Procrastinate App: OK
Worker App: OK
See Monitor Procrastinate in a real environment for more details on healthchecks.