Django 9: Scheduled tasks
Tags: python, django, docker, cron
Published: 2024-05-29 04:44:28

> NOTE: This article was initially posted on my Substack, at https://andresalvareziglesias.substack.com/

Hi all!

It’s been a while since the last post. The real life can be problematic sometimes… but we are here again! Ready to continue with our little experiment?

In the first eight chapters of this series, we built a full (but basic) player versus player web based multiplayer tic tac toe. Now, we need to finish the experiment with a CPU player based on a machine learning algorithm.

But first, we need an internal scheduler, to allow the CPU player (our dragons) to play asynchronously, without the need of human interaction.

## Articles in this series

Chapter 1: [Let the journey start](https://andresalvareziglesias.substack.com/p/let-the-journey-start-with-python-docker-and-ai?utm_source=post-toc)
Chapter 2: [Create a containerized Django app with Gunicorn and Docker](https://andresalvareziglesias.substack.com/p/create-a-containerized-django-app?utm_source=post-toc)
Chapter 3: [Serve Django static files with NGINX](https://andresalvareziglesias.substack.com/p/serve-django-static-files-with-nginx?utm_source=post-toc)
Chapter 4: [Adding a database to our stack](https://andresalvareziglesias.substack.com/p/adding-a-database-to-our-stack?utm_source=post-toc)
Chapter 5: [Applications and sites](https://andresalvareziglesias.substack.com/p/django-5-applications-and-sites)
Chapter 6: [Using the Django ORM](https://andresalvareziglesias.substack.com/p/django-6-using-the-django-orm)
Chapter 7: [Users login, logout and register](https://andresalvareziglesias.substack.com/p/django-7-users-login-logout-and-register?utm_source=profile&utm_medium=reader2)
Chapter 8: [Implementing the game in Player vs Player](https://andresalvareziglesias.substack.com/p/django-8-implementing-the-game-in?utm_source=profile&utm_medium=posttoc)

![A flying dragon over a game board](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5zu77sj3a6p23y2mghso.png)

## Adding a scheduler module

There are a lot of Django scheduler out there. We will use django-crontab (https://pypi.org/project/django-crontab/) a very simple cron-based scheduler for our Django site.

First, we need to add django-crontab to our requirements.txt file:

```
Django
gunicorn
psycopg2-binary
django-createsuperuserwithpassword
pretty_errors
django-crontab
```

Then, in settings.py, add scheduler to INSTALLED_APPS:

```
INSTALLED_APPS = [
   [...],
   "django_createsuperuserwithpassword",
   "django_crontab",
   "game",
]
```

Edit the Django container entrypoint shell (entrypoint.sh) to add all defined cron tasks at container start:

```
echo "Adding Django scheduled tasks..."
python manage.py crontab add
```

NOTE: when executed, Django manages the local Linux cron service, adding or removing tasks as needed. So, we need that our base image supports cron. Edit the main Dockerfile:

```
FROM python:3.12.2-bookworm

# Install cron service
RUN apt-get update && apt-get -y install cron vim

# set some environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# install dependencies
RUN pip install --upgrade pip
COPY ./app/requirements.txt .
RUN pip install -r requirements.txt
COPY ./app/entrypoint.sh .
RUN chmod +x entrypoint.sh

# Execute cron service after migration
RUN mkdir /cron
RUN service cron restart

# copy project
COPY ./app/src/ticmagicalline .
ENTRYPOINT [ "/entrypoint.sh" ]
```

This will install the cron service on container creation.

## Define tasks

Generate a file named cron.py inside the desired Django subapp (game/cron.py) and write the scheduled task that will make the CPU movements:

```
def makePendingDragonMovement():
    print("Scheduled task executed!")
```

Once created, we need to define them in settings.py:

```
CRONJOBS = [
    ('* * * * *', 'game.cron.makePendingDragonMovement')
]
```

The cron tasks definition accepts extra arguments. We can use these arguments to redirect the output to a file, like:

```
CRONJOBS = [
    ('* * * * *', 'game.cron.makePendingDragonMovement', '>> /cron/movements.log 2>&1)
]
```

We can also use internal Django commands as cron jobs, it’s very useful!

```
CRONJOBS = [
    […]
    ('0 * * * *', 'django.core.management.call_command', [
        'clearsessions'
    ]),
]
```

The format of time is the same as in the regular system cron tasks. Read the Wikipedia for a full explanation:

[https://en.wikipedia.org/wiki/Cron](https://en.wikipedia.org/wiki/Cron)

## Regenerate the container

Regenerate the app container:

![Regenerate the app container](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ugs1aa2u9fx71g190v04.png)

And restart all containers again:

![Restart all containers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7vo41ki2ebozr6ou98rh.png)

Login in the container to see the crontab generated by django-crontab:

```
exec -it app /bin/bash
/usr/bin/crontab -l
```

And also the tasks output:

```
exec -it app /bin/bash
cd /cron
tail -F movements.log
```

## In the last chapter…

This serie of 10 posts comes to an end in the next chapter. We have learned the basics of a Django full stack app, from the backend to the frontend. Also, we have packaged our app as a container in a fully containerized runtime environment with Gunicorn anf NGINX.

But still one chapter remains. One chapter full of dragons…

## About the list

Among the Python and Docker posts, I will also write about other related topics (always tech and programming topics, I promise... with the fingers crossed), like:

- Software architecture
- Programming environments
- Linux operating system
- Etc.

If you found some interesting technology, programming language or whatever, please, let me know! I'm always open to learning something new!

## About the author

I'm Andrés, a full-stack software developer based in Palma, on a personal journey to improve my coding skills. I'm also a self-published fantasy writer with four published novels to my name. Feel free to ask me anything!