1. Topics
  2. /
  3. Server-side frameworks
  4. /
  5. Guide to Django Channels: What it is, pros and cons and use cases
13 min read

Guide to Django Channels: What it is, pros and cons and use cases

This article is a deep dive into Django Channels - a project that extends the capabilities of the Django framework, allowing Python developers to build and deliver realtime web functionality for use cases like realtime chat and multiplayer collaboration. We’ll cover the following:

Copy link to clipboard

Introduction to Django Channels

Django is an open-source, high-level Python framework for web development. It’s known to be reliable, fast, and secure. The Django framework is built on Web Server Gateway Interface (WSGI), an interface for Python applications to handle synchronous requests. 

And here’s where Django Channels (or just Channels) comes in. Unlike the core Django framework, Channels isn’t underpinned by WSGI; instead, it’s built on a newer Python spec called Asynchronous Server Gateway Interface (ASGI) which supports both synchronous and asynchronous style programming. Channels extends Django’s capabilities, making it possible to handle not only HTTP, but also WebSockets (and other protocols, like MQTT). 

Since its release in 2016, Channels has opened a new world of possibilities for Django developers, allowing them to build and deliver realtime features like chat, powered by WebSockets.

Django Channels consists of several packages:

  • Channels – the Django integration layer.

  • Daphne – the HTTP and WebSocket termination server. Daphne supports automatic negotiation of protocols, and there’s no need for URL prefixing to determine WebSocket vs. HTTP endpoints. 

  • asgiref – a package that includes ASGI base libraries for sync-to-async and async-to-sync function wrappers, server base classes, and a WSGI-to-ASGI adapter. 

  • channels_redis – optional package providing a channel layer backend that uses Redis as a backing store. See the next section for more details about channels and channel layers. 

Copy link to clipboard

Key Django Channels concepts and features

We’ll now cover Django Channels’ main concepts and features. See the official documentation to gain a more in-depth understanding of how Django Channels works under the hood.  

Copy link to clipboard

Channels and consumers 

A core concept of Django Channels is… channels.  At its core, a channel is an ordered queue with message expiry, supporting at-most-once message delivery semantics. A channel is like a pipe - a producer sends a message to this pipe from one end, and it reaches a listener at the other end.

Consumers (the equivalent of Django views) are ASGI applications. Consumers do a couple of things in particular:

  • Structure your code as functions that are called whenever an event happens (as opposed to forcing you to write event loops). 

  • Allow you to write both sync, as well as async code.

Here’s a basic example of a sync consumer which accepts incoming WebSocket connections and then sends a message:

from channels.consumer import SyncConsumer

class EchoConsumer(SyncConsumer):

    def websocket_connect(self, event):
        self.send({
            "type": "websocket.accept",
        })

    def websocket_receive(self, event):
        self.send({
            "type": "websocket.send",
            "text": event["text"],

An async consumer is quite similar; however, handler methods must be coroutines, as shown in the following code:

from channels.consumer import AsyncConsumer

class EchoConsumer(AsyncConsumer):

    async def websocket_connect(self, event):
        await self.send({
            "type": "websocket.accept",
        })

    async def websocket_receive(self, event):
        await self.send({
            "type": "websocket.send",
            "text": event["text"],
        })

Note: It’s recommended to use sync consumers by default. Async consumers should only be used in some limited scenarios (e.g., you have long-running tasks that could be done in parallel, or you’re only using async-native libraries). 

Django Channels comes with several generic consumers:

  • WebSocketConsumer - sync WebSocket consumer; allows you to handle text and binary frames.

  • AsyncWebSocketConsumer - provides the same capabilities as WebSocketConsumer, but everything is async.

  • JsonWebSocketConsumer - works like WebSocketConsumer, while also allowing you to import JSON data into your Django app. 

  • AsyncJsonWebSocketConsumer - async version of JsonWebSocketConsumer.

  • AsyncHTTPConsumer - offers basic primitives to implement HTTP endpoints (including Server-Sent Events endpoints). 

Copy link to clipboard

Channel layer

A channel layer is a mechanism that allows multiple instances of your Django application to communicate and exchange messages. Channel layers are especially useful when you’re building distributed applications, and you don’t want all your messages to go through a database. You can also use channel layers in combination with a worker process to create basic task queues/offload tasks. 

Per the Django Channels documentation:

“The channel layer is for high-level application-to-application communication. When you send a message, it is received by the consumers listening [...] on the other end. [...] you should send high-level events over the channel layer, and then have consumers handle those events, and do appropriate low-level networking to their attached client”.

For example, if you’re building a chat application with Django Channels, you could send events such as this one via the channel layer:

await self.channel_layer.group_send(
    room.group_name,
    {
        "type": "chat.message",
        "room_id": room_id,
        "username": self.scope["user"].username,
        "message": message,
    }
)

You would then define a handling function on the consumer side to receive the events and transform them into WebSocket frames:

async def chat_message(self, event):
    """
    Called when someone has messaged our chat.
    """
    # Send a message down to the client
    await self.send_json(
        {
            "msg_type": settings.MSG_TYPE_MESSAGE,
            "room": event["room_id"],
            "username": event["username"],
            "message": event["message"],
        },
    )

Note that channel layers are optional, and they have an async interface. You can configure channel layers through the CHANNEL_LAYERS setting (leave it unset if you don’t want to use any channel layer). 

Django Channels v1 provided multiple options to use as the channel layer: Redis, ICP, an in-memory layer, and a RabbitMQ channel backend. However, starting with v2 (at the time of writing, version Django Channels has reached version 4.x), only one channel layer for production use is provided: channels_redis, which uses Redis as a backing store. Channels_redis comes with two implementations:

  • RedisChannelLayer - the original version.

  • RedisPubSubChannelLayer - the more recent version, which uses Redis Pub/Sub for messaging. Note that this is currently in beta, and may go through breaking changes in the future. 

Beyond the Redis channel layer backend, Django Channels also offers an in-memory channel layer; however, this is only useful when running a local Django development server (for testing purposes). The in-memory channel layer is not a valid option for production-ready systems.  

Django Channels provides a broadcast mechanism, which is very useful when you want to send the same message to multiple consumers. Think, for example, of use cases like chat rooms, where messages need to be sent to all participants; or simultaneously streaming live score updates to N users.

Django Channel’s built-in broadcast mechanism is known as groups. This mechanism allows you to add/remove channels from named groups, and send messages to these groups (and implicitly, to all channels that are part of a group). 

This example shows how to add a channel to a group:

class ChatConsumer(WebsocketConsumer):

    def connect(self):
        async_to_sync(self.channel_layer.group_add)("chat", self.channel_name)

And here’s how you broadcast messages to all channels that are part of the group:

class ChatConsumer(WebsocketConsumer):

    ...

    def receive(self, text_data):
        async_to_sync(self.channel_layer.group_send)(
            "chat",
            {
                "type": "chat.message",
                "text": text_data,
            },
        )

    def chat_message(self, event):
        self.send(text_data=event["text"])
Copy link to clipboard

Scopes and events

Django Channels splits any incoming HTTP and WebSockets connection into two distinct components: a scope, and a series of events. The scope contains connection-specific details such as request path, method, headers (for HTTP), or the originating IP address (for WebSocket). It’s important to bear in mind that the scope lasts a single request for HTTP, while for WebSockets, it lasts as long as the connection itself (although the scope changes if the socket closes and then reconnects). 

During the lifetime of a scope, multiple events (user interactions) may occur. Your Django Channels applications will be instantiated once per scope; there is a single application instance handling all events associated with a scope. 

Django Channels comes with routing classes, which allow you to combine multiple consumers (ASGI applications) into one bigger app. A couple of things to remember about Channel routers:

  • They are valid ASGI applications themselves.

  • They work on a scope level, and you can only have one consumer for any given connection; routing is not designed to spread events from one connection across multiple consumers.

While it’s possible to write your own routers, or use routers provided by third-party apps, Django Channels comes with some in-built routing options:

  • ProtocolTypeRouter - allows you to route connections based on the protocol used (HTTP or WebSocket).

  • URLRouter - routes HTTP and WebSocket connections based on their path. 

  • ChannelNameRouter - routes connections based on the channel name.

Copy link to clipboard

Authentication

Django Channels supports standard Django authentication for HTTP and WebSocket via  AuthMiddleware. Additionally, you can write your own custom middleware if you wish to use a different authentication scheme (such as tokens passed in the URL).  

It’s also worth mentioning that Django Channels offers login and logout functions: channels.auth.login and channels.auth.logout, which are quite similar to Django’s contrib.authpackage

For more details about Django Channels authentication, refer to the official documentation.

Copy link to clipboard

Django Channels architecture

A basic Django Channels architecture might look something like this:

Producers (browser clients in our example) send messages to channels for consumers listening to those channels to receive. When a browser client initiates a connection, it connects to an interface server (an ASGI server, such as Daphne). The connection is then forwarded to a router app (part of the channel layer) that checks if it's an HTTP connection or a WebSocket one. The channel layer - which uses Redis as a backing store -  then sends messages to the appropriate consumer (WebSocket or HTTP consumer). Of course, consumer apps can follow the same flow (in reverse) to send messages back to browser clients.

Copy link to clipboard

Realtime use cases for Django Channels

Django Channels allows you to build realtime apps, often using WebSocket as the main transport protocol. WebSockets (and implicitly, Django Channels) use cases can be broadly grouped into two distinct categories:

  • Realtime updates, where the communication is unidirectional, and the ASGI Django server needs to send low-latency (and often frequent) updates to the client. Think of broadcasting live score updates, GPS location tracking, powering live dashboards, or sending realtime alerts and notifications, to name just a few use cases.

  • Bidirectional communication, where both the client and the server send and receive messages. Examples include chat, multiplayer collaboration, virtual events, and virtual classrooms (the last two usually involve features like live polls, quizzes, and Q&As). 

Judging by the tutorials and demos available online, chat apps are by far the most common use case for Django Channels. Other popular examples include ride-sharing apps, photo-sharing apps, polling apps, and multiplayer collaboration tools. See a collection of projects built with Django Channels

Copy link to clipboard

Django Channels advantages

Flexibility

Django Channels offers you a certain degree of flexibility, so you can configure your new Django project in a way that makes the most sense for your use case. First of all, Django Channels has multi-protocol support: you can use WebSockets, HTTP (including HTTP/2, long-polling, and Server-Sent Events), and other options like chat and IoT protocols. 

Secondly, Channels supports a 1:1 messaging pattern, but it also allows you to broadcast events to multiple consumers via groups - which we’ve covered earlier in the article. Thirdly, Django Channels provides both synchronous, as well as asynchronous consumers.  

It’s a mature, stable technology

Django Channels has been around since 2016, and it’s being actively maintained. At the time of writing (December 2022), Django Channels has reached v 4.x. There have been very few breaking changes from version to version, which seems to indicate that Channels is in a stable place. 

It’s also worth pointing out that Channels is an extension of the Django framework, so your Channels projects will use a lot of native Django capabilities. Django itself was first released publicly in 2005. Over the years, it has matured into a reliable solution, which has been extensively battle-tested in production. Django is a solid foundation for building realtime, WebSocket-based functionality on top.   

It’s easy to get started with Channels

Django (and by extension Channels) is known to be easy to learn and get started with. Django aims to make it easier to build web apps, with less code. It focuses on the reusability of components, and it’s highly extensible, with thousands of plugins you can incorporate into your project.   

Django also benefits from a huge developer community. There are hundreds of tutorials available showing you how to use Django and its Channels extension. And if you come across issues while developing your apps, it’s likely someone from the Django community has gone through similar experiences and will be able to help you troubleshoot.

Copy link to clipboard

Django Channels disadvantages

Lacks certain capabilities

Although it’s a robust framework with plenty of useful features for realtime web development, Django Channels is a general-purpose solution. It lacks some built in capabilities that would reduce the time required to develop and launch new realtime functionality.

For example, if you’re building a chat app (this seems to be the most frequent use case for Django Channels), you’d probably want presence, which is useful for displaying and updating the status of users as they come online and go offline. Another common chat feature is the ability to react to messages (emoji reactions). Unfortunately, Django Channels doesn’t offer presence or message interactions out of the box; these are additional capabilities you have to integrate/build and manage yourself, leading to increased engineering overhead. 

Message ordering and delivery are not guaranteed

Data integrity (guaranteed message ordering and delivery) is desirable, if not critical for many use cases. Imagine how frustrating it must be for chat users to receive messages out of order. Or think of the impact an undelivered fraud alert can have.   

If data integrity is crucial for your use case, you should be aware that Django Channels comes with some limitations. First of all, channels only provide at-most-once delivery semantics, which means that some messages may never be delivered. Secondly, ordering is only guaranteed if the channel has a single reader and writer. 

Learn more about Django Channel’s messaging semantics

It’s hard and expensive to manage at scale

Whether you use Django Channels or any other similar solution to deliver realtime features, building a reliable DIY WebSocket solution that you can trust to deliver at scale is expensive, time-consuming, and requires significant engineering effort. You have to host and manage the Django project yourself, think about how you’re going to scale it, and ponder how to ensure it’s highly available and fault tolerant. 

Here are some key stats to give you a taste of how complex it is to engineer WebSocket capabilities in-house:

  • 65% of self-built WebSocket solutions had an outage or significant downtime in the last 12-18 months.

  • 10.2 person-months is the average time to build basic WebSocket infrastructure, with limited scalability, in-house.

  • Half of all self-built WebSocket solutions require $100K-$200K a year in upkeep.

Copy link to clipboard

Alternatives to Django Channels

We hope this article helps you understand how Django Channels works and allows you to assess if it’s the right choice for your realtime project. Django Channels has its advantages: it supports several protocols - primarily WebSockets, but also HTTP, chat, and IoT protocols; it allows you to do both synchronous, as well as asynchronous programming; it’s easy to get started with, especially if you’re familiar with the Django framework.

However, Django Channels also comes with some disadvantages - for example, message ordering and delivery are not guaranteed. It’s also worth noting that Django Channels is likely to be expensive and hard to manage at scale in-house, just like any other WebSocket-based system. Learn about the challenges of scaling WebSockets.

If you’re planning to build and deliver realtime features at scale, it is ultimately up to you to decide if Django Channels is the best choice for your use case, or if a Django Channels alternative is a better fit. 


About Ably

Ably is a realtime experience infrastructure provider, helping developers power multiplayer, chat, data synchronization, data broadcast, and notifications at internet scale. We provide a globally-distributed network, client SDKs targeting every major programming language, and diverse capabilities, including hosted realtime APIs (WebSocket-based), pub/sub messaging, presence, and guaranteed message ordering and delivery.    

Join the Ably newsletter today

1000s of industry pioneers trust Ably for monthly insights on the realtime data economy.
Enter your email