DEV Community

Andrés Álvarez Iglesias
Andrés Álvarez Iglesias

Posted on

Django 9: Scheduled tasks

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
Chapter 2: Create a containerized Django app with Gunicorn and Docker
Chapter 3: Serve Django static files with NGINX
Chapter 4: Adding a database to our stack
Chapter 5: Applications and sites
Chapter 6: Using the Django ORM
Chapter 7: Users login, logout and register
Chapter 8: Implementing the game in Player vs Player

A flying dragon over a game board

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
Enter fullscreen mode Exit fullscreen mode

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

INSTALLED_APPS = [
   [...],
   "django_createsuperuserwithpassword",
   "django_crontab",
   "game",
]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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" ]
Enter fullscreen mode Exit fullscreen mode

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!")
Enter fullscreen mode Exit fullscreen mode

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

CRONJOBS = [
    ('* * * * *', 'game.cron.makePendingDragonMovement')
]
Enter fullscreen mode Exit fullscreen mode

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)
]
Enter fullscreen mode Exit fullscreen mode

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

CRONJOBS = [
    […]
    ('0 * * * *', 'django.core.management.call_command', [
        'clearsessions'
    ]),
]
Enter fullscreen mode Exit fullscreen mode

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

Regenerate the container

Regenerate the app container:

Regenerate the app container

And restart all containers again:

Restart all containers

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

exec -it app /bin/bash
/usr/bin/crontab -l
Enter fullscreen mode Exit fullscreen mode

And also the tasks output:

exec -it app /bin/bash
cd /cron
tail -F movements.log
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)