Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 62

Integrating Celery and Redis with Django

Recommended resources

Hello! Welcome back to our module on Asynchronous Tasks and Caching.

In our previous lesson, we explored the "why" behind asynchronous task queues. We established that offloading long-running operations is crucial for building responsive, scalable, and reliable applications. We identified the core components of this architecture: a producer (our Django app), a message broker, and a consumer (a worker process).

Today, we move from theory to practice. This lesson is dedicated to achieving the learning outcome: Integrate Celery with a Django project using Redis as the message broker. We will set up all the necessary "plumbing" to connect these components, laying the foundation for running our first background task.

Your background in developing complex AI systems like Document Verification pipelines and RAG-based AI coaches gives you a unique perspective. Many of the heavy-lifting operations in those systems—such as processing a large document, running a complex inference, or fine-tuning a model—are perfect candidates for the asynchronous processing we're about to implement. This setup is the backend backbone for such features.

Django, Celery, and Redis Architecture for AI Applications
This diagram shows a modern backend for an AI application. Notice how Celery workers, managed by a Redis message broker, are used to handle intensive tasks like LLM inference. This is precisely the type of architecture we are starting to build.

The Three Moving Parts

To get our asynchronous system working, we need to set up and connect three main services:

  1. The Django Application (Producer): The source of our tasks.
  2. Redis (Message Broker): The middleman that holds tasks until a worker is ready. It will also serve as our result backend to store task outcomes.
  3. The Celery Worker (Consumer): The separate process that executes the tasks.

Let's get them all working together.


Step 1: Install Required Packages

First, we need to install Celery and the Python client library for Redis. Ensure your project's virtual environment is activated and run the following command:

pip install celery redis
  • celery: The asynchronous task queue framework itself.
  • redis: The Python client library that allows our Django application and Celery to communicate with the Redis server.

Step 2: Install and Run the Redis Server

Redis is an in-memory data store that is not part of Python; it's a separate server process. You need to install and run it on your system.

To understand the role of Redis and how to install it, please review the following resource.

Asynchronous Tasks With Django and Celery

The article 'Asynchronous Tasks With Django and Celery' by Real Python provides an excellent, non-Docker guide to this process. We will use it as our primary reference for the setup.

Read the section 'Install Redis as Your Celery Broker and Database Back End'. This will guide you through installing Redis on your operating system (use Homebrew for macOS or apt for Debian/Ubuntu) and verifying the installation.

After following the installation instructions, you will need two separate terminal windows for the next steps:

  1. Terminal 1: Start the Redis Server. Keep this running in the background.
    redis-server
    
  2. Terminal 2: Verify Redis is running. You can use the Redis Command-Line Interface (CLI) to send a ping command to the server. It should respond with PONG.
    redis-cli
    ping
    # Expected output: PONG
    
    You can exit the redis-cli by pressing Ctrl+C.

With Redis running, we now have our message broker ready to receive messages.


Step 3: Integrate Celery into Your Django Project

Now we need to configure our Django project to know about Celery and how to communicate with Redis. This involves creating a Celery instance and pointing it to the correct settings.

We will add three small pieces of code to our project, following the best practices recommended by the Celery documentation.

Asynchronous Tasks With Django and Celery

Let's return to the Real Python article. This section details the three crucial files you need to modify to instantiate and configure Celery within Django.

Read the section 'Add Celery to Your Django Project'. Follow the steps to: Create the celery.py file in your main project directory (alongside settings.py). Add the Celery configuration variables (CELERY_BROKER_URL and CELERY_RESULT_BACKEND) to your settings.py file. Update the __init__.py file in your main project directory.

Let's break down what you've just done:

  1. myproject/celery.py:

    • This file creates a Celery application instance.
    • os.environ.setdefault(...): Ensures Django's settings are available to Celery.
    • app = Celery("myproject"): Creates the instance, named after our project.
    • app.config_from_object("django.conf:settings", namespace="CELERY"): Tells Celery to look for its configuration inside our Django settings.py file, but only for settings that start with the prefix CELERY_.
    • app.autodiscover_tasks(): A powerful command that tells Celery to automatically find any tasks defined inside a tasks.py file within each app of your project.
  2. myproject/settings.py:

    • CELERY_BROKER_URL = "redis://localhost:6379": This is the most critical setting. It tells Celery where the message broker is listening.
    • CELERY_RESULT_BACKEND = "redis://localhost:6379": This tells Celery where to store the state and return values of tasks. While optional, it's highly recommended. We are conveniently using the same Redis instance for both roles.
  3. myproject/__init__.py:

    • This ensures that your Celery app instance is loaded whenever Django starts up, making it available throughout your project.
Test your understanding!

In settings.py, we set both CELERY_BROKER_URL and CELERY_RESULT_BACKEND to point to the same Redis instance.

  1. Could you use two different services for these roles? For example, RabbitMQ for the broker and a PostgreSQL database for the result backend?
  2. If so, what would you need to change in settings.py?
Show answer
  1. Yes, you absolutely can use different services. It's a common pattern to use a message broker optimized for fast message delivery (like RabbitMQ or Redis) and a persistent database (like PostgreSQL or MySQL) for the result backend if you need to store and query task results for longer periods.

  2. You would change the URLs in settings.py to point to the respective services. For example:

    # Using RabbitMQ as the broker
    CELERY_BROKER_URL = "amqp://user:password@hostname:5672/vhost"
    
    # Using the django-celery-results library with PostgreSQL
    CELERY_RESULT_BACKEND = "django-db" 
    

    This separation allows you to choose the best tool for each job. Using Redis for both is simply a convenience for getting started.


Step 4: Verify the Integration

The setup is complete. The final step is to run all the components and see if they can communicate. You will need three separate terminals, all with your virtual environment activated (except for the Redis server terminal).

  1. Terminal 1: Redis Server (should already be running from Step 2)

    redis-server
    
  2. Terminal 2: Django Development Server

    python manage.py runserver
    
  3. Terminal 3: Celery Worker
    Start the Celery worker process. It needs to know where your Celery application instance is. Replace myproject with the actual name of your Django project folder (the one containing settings.py).

    celery -A myproject worker --loglevel=info
    
    • -A myproject: The -A flag specifies the application instance to use.
    • worker: This command tells Celery to start a worker process.
    • --loglevel=info: This sets the logging level to provide useful output.

When the worker starts, you should see a text-based "banner" for Celery, followed by output indicating it has successfully connected to your Redis server. It will look something like this:

 -------------- celery@your-hostname v5.x.x (stars)
--- ***** ----- 
-- ******* ---- Linux-5.15.0-78-generic-x86_64-with-glibc2.35 2023-08-01 12:00:00
- *** --- * --- 
- ** ---------- [config]
- ** ---------- .> app:         myproject:0x7f8d1c7a3fa0
- ** ---------- .> transport:   redis://localhost:6379//
- ** ---------- .> results:     redis://localhost:6379/
- *** --- * --- .> concurrency: 8 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** ----- 
 -------------- [queues]
                .> celery           exchange=celery(direct) key=celery


[tasks]

[2023-08-01 12:00:01,000: INFO/MainProcess] Connected to redis://localhost:6379//
[2023-08-01 12:00:01,000: INFO/MainProcess] celery@your-hostname ready.

The key lines are transport: redis://... and Connected to redis://.... If you see these, your integration is successful! The worker is now idle, listening for tasks on the queue. The [tasks] section is empty because we haven't defined any yet.


Conclusion

Congratulations! You have successfully configured a robust, industry-standard system for asynchronous task processing. While it involved several steps, you've now established the essential infrastructure that powers countless high-performance web applications.

Key Takeaways:

  • A complete Celery setup requires three running processes: your Django app, the Redis broker, and the Celery worker.
  • The integration happens in three key files:
    • celery.py creates the Celery app instance.
    • settings.py configures the CELERY_BROKER_URL to point Celery to Redis.
    • __init__.py ensures the Celery app loads with Django.
  • The command celery -A myproject worker starts the consumer process that will execute your background tasks.

Preview of the Next Lesson:
With the "plumbing" in place, we're ready for the exciting part. In the next lesson, "Define and execute a background task using Celery to improve API response times," we will create our first task in a tasks.py file, call it from one of our views, and watch our Celery worker pick it up and execute it in the background.