Skip to content

Commit df4cf0c

Browse files
committed
Sample code for the article on Django Tasks
1 parent e3048d1 commit df4cf0c

14 files changed

Lines changed: 258 additions & 0 deletions

File tree

django-tasks/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Django Tasks: Exploring the Built-in Task Framework in Django 6
2+
3+
Example project for the Real Python tutorial [Django Tasks: Exploring the Built-in Task Framework in Django 6](https://realpython.com/django-tasks/).
4+
5+
The project demonstrates Django 6's `@task` decorator, the `django-tasks-db` database-backed worker, named queues with priorities, and the `transaction.on_commit` pattern for safe enqueueing inside an atomic block.
6+
7+
## Setup
8+
9+
Set up a virtual environment and install dependencies:
10+
11+
```sh
12+
$ python -m venv venv
13+
$ source venv/bin/activate
14+
(venv) $ python -m pip install -r requirements.txt
15+
```
16+
17+
Run migrations:
18+
19+
```sh
20+
(venv) $ python -m manage makemigrations myapp
21+
(venv) $ python -m manage migrate
22+
```
23+
24+
## Run
25+
26+
The project uses two processes: the Django dev server and the `db_worker`.
27+
28+
In one terminal, start the worker:
29+
30+
```sh
31+
(venv) $ python -m manage db_worker --queue-name '*'
32+
```
33+
34+
In a second terminal, start the dev server:
35+
36+
```sh
37+
(venv) $ python -m manage runserver
38+
```
39+
40+
Trigger the demo endpoints with `curl` or your browser:
41+
42+
```sh
43+
$ curl 'http://localhost:8000/register/?email=alice@example.com&name=Alice'
44+
$ curl http://localhost:8000/checkout/
45+
$ curl http://localhost:8000/task-status/<task-id>/
46+
```
47+
48+
You can also enqueue tasks from the Django shell:
49+
50+
```sh
51+
(venv) $ python -m manage shell
52+
```
53+
54+
```pycon
55+
>>> from myapp.tasks import say_hello
56+
>>> result = say_hello.enqueue("Real Python")
57+
>>> result.refresh()
58+
>>> result.status
59+
SUCCESSFUL
60+
>>> result.return_value
61+
'Hello, Real Python!'
62+
```

django-tasks/config/__init__.py

Whitespace-only changes.

django-tasks/config/settings.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from pathlib import Path
2+
3+
BASE_DIR = Path(__file__).resolve().parent.parent
4+
5+
SECRET_KEY = "django-insecure-demo-key-do-not-use-in-production"
6+
DEBUG = True
7+
ALLOWED_HOSTS = ["*"]
8+
9+
INSTALLED_APPS = [
10+
"django.contrib.contenttypes",
11+
"django.contrib.auth",
12+
"django_tasks_db",
13+
"myapp",
14+
]
15+
16+
MIDDLEWARE = []
17+
18+
ROOT_URLCONF = "config.urls"
19+
20+
DATABASES = {
21+
"default": {
22+
"ENGINE": "django.db.backends.sqlite3",
23+
"NAME": BASE_DIR / "db.sqlite3",
24+
}
25+
}
26+
27+
TASKS = {
28+
"default": {
29+
"BACKEND": "django_tasks_db.DatabaseBackend",
30+
"QUEUES": ["default", "emails", "reports"],
31+
},
32+
}
33+
34+
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
35+
DEFAULT_FROM_EMAIL = "noreply@example.com"
36+
37+
USE_TZ = True
38+
TIME_ZONE = "UTC"
39+
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

django-tasks/config/urls.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.urls import include, path
2+
3+
urlpatterns = [
4+
path("", include("myapp.urls")),
5+
]

django-tasks/manage.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env python
2+
"""Django's command-line utility for administrative tasks."""
3+
import os
4+
import sys
5+
6+
7+
def main():
8+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
9+
try:
10+
from django.core.management import execute_from_command_line
11+
except ImportError as exc:
12+
raise ImportError(
13+
"Couldn't import Django. Are you sure it's installed and "
14+
"available on your PYTHONPATH environment variable? Did you "
15+
"forget to activate a virtual environment?"
16+
) from exc
17+
execute_from_command_line(sys.argv)
18+
19+
20+
if __name__ == "__main__":
21+
main()

django-tasks/myapp/__init__.py

Whitespace-only changes.

django-tasks/myapp/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class MyappConfig(AppConfig):
5+
default_auto_field = "django.db.models.BigAutoField"
6+
name = "myapp"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Generated by Django 6.0.3 on 2026-05-03 11:08
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
initial = True
9+
10+
dependencies = [
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name='Order',
16+
fields=[
17+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18+
('item', models.CharField(default='widget', max_length=100)),
19+
('created_at', models.DateTimeField(auto_now_add=True)),
20+
],
21+
),
22+
migrations.CreateModel(
23+
name='User',
24+
fields=[
25+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
26+
('email', models.EmailField(max_length=254)),
27+
('name', models.CharField(blank=True, max_length=100)),
28+
],
29+
),
30+
]

django-tasks/myapp/migrations/__init__.py

Whitespace-only changes.

django-tasks/myapp/models.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from django.db import models
2+
3+
4+
class User(models.Model):
5+
email = models.EmailField()
6+
name = models.CharField(max_length=100, blank=True)
7+
8+
def __str__(self):
9+
return self.email
10+
11+
12+
class Order(models.Model):
13+
item = models.CharField(max_length=100, default="widget")
14+
created_at = models.DateTimeField(auto_now_add=True)
15+
16+
def __str__(self):
17+
return f"Order {self.pk} ({self.item})"

0 commit comments

Comments
 (0)