Sketching out a Django redesign

This video features Tom Christie at DjangoCon Europe 2019 in Copenhagen, Denmark.

Sketching out a Django redesign
0:53:30
Published April 23, 2019
5,643 views

Summary

Python’s async/await model can handle far more concurrent work than threads, enabling high-throughput APIs, non-blocking network calls, WebSockets, server-sent events, and resilient services. Tom Christie explains that async brings real costs: explicit I/O, incompatible synchronous and asynchronous call chains, missing database and library support, and a need to manage connections and CPU-heavy work carefully. He presents ASGI as a shared, composable interface for servers, frameworks, middleware, testing, and protocols, alongside async database tools and an emerging Django-like ORM. Django could adopt this progressively—starting with an ASGI layer and thread pools, then making more of the stack asynchronous—while the wider community supports the work through shared standards, sponsorship, and sustainable products. He closes by arguing that funding open source is a means to create useful technology and broader social impact, not an end in itself.

Key takeaways

  • Async tasks run within event loops and can handle I/O-heavy concurrency more efficiently than operating-system threads.
  • ASGI supports HTTP, WebSockets, server-sent events, lifecycle events, and reusable components across Python web frameworks.
  • Explicit asynchronous I/O prevents hidden database work, making performance problems easier to detect while requiring more deliberate code.
  • Async support needs to extend beyond views to databases, HTTP clients, caching, validation, email, and CPU-heavy operations such as password hashing.
  • Django can adopt async incrementally by adding ASGI support first and gradually converting middleware and other components.
  • Shared standards and sustainable funding are important for maintaining the ecosystem and ensuring open-source work has lasting social impact.

Summarised automatically from the transcript.

Chapters

  1. 0:00 Async Python and Django Tom Christie introduces the talk and explains why asynchronous Python matters for scalable web services and real-time applications.
  2. 2:19 Concurrency Models An overview of horizontal scaling, processes, threads, and the async task model.
  3. 7:29 Async Trade-offs The talk examines async syntax, ecosystem migration costs, latency considerations, and when async is worthwhile.
  4. 10:54 Async Performance Christie discusses the benefits of async for high-throughput services, real-time connections, resilience, and Python’s performance position.
  5. 14:44 From WSGI to ASGI The limitations of WSGI lead into Django Channels and the design of ASGI as an asynchronous, protocol-flexible interface.
  6. 21:02 ASGI Framework Design The emerging ASGI server and framework ecosystem is introduced, along with the design goals behind Starlette.
  7. 22:37 Composable ASGI Components Examples show how ASGI enables reusable requests, test clients, middleware, mountable applications, class-based views, and per-component configuration.
  8. 30:30 Async Databases and ORMs Christie presents async database interfaces, SQLAlchemy Core integration, and a Django-like asynchronous ORM.
  9. 36:40 Async Resource Management The talk covers explicit database access, connection lifetimes, async HTTP and caching, validation, and CPU-bound password hashing.
  10. 40:30 Full-Stack Async Architecture A complete async web stack is assembled, highlighting configuration, validation, routing, middleware, performance, and real-time capabilities.
  11. 44:34 Django’s Async Roadmap Christie outlines a gradual plan for adding ASGI and async capabilities to Django while preserving compatibility.
  12. 47:41 Open-Source Sustainability The closing section discusses sponsorship, product opportunities, community investment, and the broader purpose of open source.

Transcript

7,214 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:00

We're there. Okay. Um yeah, one last thing before I get started. Uh I think my kids might be watching on the livestream at home. So hi boys, hi yeah, hi kids, thank you. Okay, great. Hi, thank you very much. My name is Tom Christie. I've been a longtime Python and Django user. I'm the maintainer of several open source projects, and I'm most well known for being the author of Django Rest Framework. Now, I'm fortunate enough to be in a position where I work on open source full-time now for my day job. Django Rest Framework launched a sponsorship program.

0:47

Where lots and lots of different companies can contribute a small amount, a small amount per month, and this is what pays for my day job at the moment. And I've been spending, as well as working on Django Rest framework and helping manage that, spending a lot of time recently on async, which is what I'm going to talk about today. So Python is at a really big crossroads right now. Python 3. 5 Introduce some new functionality in the form of the async and await keywords. The async and await keywords help us use an entirely new concurrency model

1:34

That is much, much more efficient than the existing threaded concurrency model. And this is going to let us do some really exciting things. Um for one thing, it lets us build very, very high throughput web services. It allows us to make non -blocking HTTP requests. So we can build Python gateway APIs or proxy services. That are able to handle very high concurrency, high throughputs, which is a domain where Python has traditionally not excelled in. Um and it will also allow us to um

2:19

handle real-time network protocols. such as WebSockets. So we'll be able to start building more and more real-time responsive web applications, chat services, games, real-time monitoring, all of these kinds of things. Um before we dive into this, a little bit of groundwork, concurrency. What is concurrency? Concurrency is all about the number of tasks that your server is able to handle simultaneously. In the web development land, this maps on to how many HTTP connections is your server able to hold open at the same time.

3:07

and then in turn also influences what is the throughput that your server is able to achieve. How many requests per second can each individual server handle? So what are some of the ways that we have to increase the concurrency of these web services that we're building? So the bluntest hammer in our toolbox, our sledge hammer, is uh multi-host, so horizontal scaling. Where you add more servers running the same code base, and you're going to be able to handle a greater number of requests per second. Well, let's bring that down a level to a single server. How can we increase the concurrency on a single server?

3:54

Within a single server, we'll have a number of CPU cores We want to make sure that all of those CPU cores are fully utilized all the time, as much as possible, so we can run multiple processes. within each machine. Or all pretty much completely independent, but all running at the same time Okay, bring that down again another level. How do we increase the concurrency within a single process? running on a single server. So traditionally what we've used is threaded concurrency. And with threaded concurrency, what happens is we have a number of simultaneous flows of control running through our program

4:41

and each individual flow of control will have large chunks of time where it's not utilizing the CPU. Because it's waiting on I. O. from the rest of the system. So anytime your program goes off and makes an HTTP request uh makes a database access, accesses some disk I. O. There's this relatively huge chunk of time when that thread of control is not able to do anything else. until it gets a response back from the operating system. So with multi-threading, what the operating system does for you is handles interleaving several different flows of control

5:27

and switches between them very, very quickly. So it appears as if all these flows of control are happening at the same time. Now, more recently, the async model has been introduced, and the the key point to take away at at this point in time is async is an alternative to multi-threading, right? With async you will have multiple tasks rather than multiple threads And your multiple tasks will all be running within a single thread. But you will still have multiple processes, all running lots and lots of little tasks within it, and you will still be running across multiple different hosts. And async is um, as we said, far more efficient.

6:15

So what are the differences between these two? Um with threaded concurrency, everything's managed by the operating system. And the op you don't get to see as a programmer when am I going to switch between one of my two different Threads of control. In async, it's a completely different model in that it's managed by the runtime, it's managed by Python itself. Or um or by node orgo, whatever runtime you happen to be using. And the points of context switching between these different flows of control are still the points at which you're performing I/O, so network requests, database accesses, disk

7:03

accesses, but they have to be explicitly marked in the program so that the runtime knows Okay, here's a point at which I can context switch. So you have um this entirely new syntax that is introduced. the async and await keyword. And what's problematic is that these two models are largely incompatible. Yeah, if you're going to have explicit context switching, then you need to have explicit context switching all the way through. So if you're low level I'm making a network request as being explicitly marked, then anything that calls into that also

7:49

needs to be marked up as being an async function. And although there are ways that we can mediate between these two styles, it's a bit fiddly. So there's this huge uh challenge for the ecosystem. It's it's a little bit of a fork in the road. Um And it's important to acknowledge that as with any new technology, there are costs as well. And we need to talk about them up front and recognise them so that we're in a good position to judge what the trade-offs are. So there's this huge upfront

8:36

cost in terms of all of the new code that needs to be ri written in order to work with async. So for one thing, all of the low-level networking stuff, all of the database drivers or making HTTP requests. Or exactly how do you go about making disk I. O. People need to write low-level async libraries to interact with that because it it doesn't exist in the standard library, because it wasn't a thing when Python was not 3. 5. Um not quite true, but pretty much. Um What else? Okay, well it's a different paradigm, and there's a little bit more to think about as well as a as a developer.

9:22

There's two different types of function call you've got now. You've got async function calls which is anything that is making I. O. And you've got regular function calls, which is only allowed to just be doing regular programmy type stuff. It's using the CPU and it's performing some kind of computation. Um one of the other reasons that it's worthwhile being cautious here as well is you might not care about throughput. Okay, so for when you're looking at the performance of your web service The thing that your users will most care about is how long does it take, once they've made a request, for them to get a page back in front of them?

10:08

What is the latency? Of the request. And async has an influence on that, but it's more complicated. And unless you are building a very high volume service, the throughput that it's able to handle might not matter that much unless you have several app uh several server instances, why do you care that it could handle high load? Fine, so that's the words of c you know, things to bear in mind, but the benefits, right? Um sometimes the performance really does matter. And

10:54

in the cases where it does, it's really, really important that this should not be a blocker for businesses adopting Python. We want to be able to build hugely scalable web services with Python and we want the mega companies who are developing these flagship services. to be choosing Python right at the start, building highly successful products with it, and being able to go to the rest of the world, hey look at this awesome thing that company XYZ has built in Python And for them to go, yeah, this was a great development experience for our team, and we're very happy with the choice that we made. What else? Um, real time Async, uh

11:40

because async is much more resource efficient, we're able to hold open uh lots and lots of network connections. without that having a high impact. So we can hold open things like WebSockets and we could do real-time communications. Non-blocking HTTP requests being able to perform parallelisation within our codes without that being a heavyweight thing that branching lots of new threads would be The explicit I. O. is actually also a benefit as well, but we'll come to that later in the talk. And one other thing to say here is performance can mean different things in different contexts.

12:27

Right? So being able to build very highly concurrent web services, well, a flipway round of looking at that is on very lightly resourced systems, on embedded systems, these sorts of things will work really, really well. Or suppose you're mm- you don't require high throughput on your site most of the time, but suddenly you get a huge traffic spike. your service is much much more resilient to that. Okay? So it doesn't have to always be about this is just about high throughput services all the time. There are other reasons why it's important And there's this other thing, you know. It is

13:12

something that has been said lots of times. People say, oh Py Python's slow. We don't want to use Python for XYZ because Python is slow. Now Uh yeah, okay, benchmarks. Nonsense, nonsense, nonsense. The the tech empower benchmarks are the least awful ones that are around. They've got a number of different test cases. that exercise, uh various different bits of do some database stuff, do s do reads to writes. Whatever. This particular test case I think is the most representative of web applications because it does a little bit of database reading, it does a little bit of template rendering, and it does a bit of exercising the web stack And

13:57

yes, I've cut some Go results off of the top here. And yes, I've cut lots and lots of results from several different frameworks off the bottom here But the important things to look at are this one at the top. This is Go, written against the Go standard library. The ones in yellow, those are all node-based. And these ones in blue are are both Python. And we've got a single very beefy server there. that on its own is servicing servicing 70,000 requests per second, which is probably more than you need. So it's about making sure that

14:44

there isn't this blocker to business adoption. Python is in the same ballpark as Node and Go for almost all intents and purposes. that we in this room would probably need. So, okay, great. We'd like to start taking advantage of this. What do we need to do? The first thing in the stack that we run into is WISGI. WISGI is the interface that exists between the server Gunnar or UWSGI or something like that. And the uh the application framework, the web framework.

15:31

And WISGI has a couple of very big constraints for what we'd like to be able to do One of which it's inherently a thread concurrency interface. It doesn't have that async definition on there, so it's not allowed to do any async context switching inside it. And it's designed purely for handling HTTP requests and responses, so it's got no easy way to adapt it for WebSockets. So um Hello, ASCII. Along comes Django Channels, which originally was designed in order to keep set to To deal with WebSockets in Django by largely keeping separate the asynchronous nature of handling the WebSocket

16:18

connections from the synchronous nature of the thread-based Django code base. And it has gradually evolved under Andrew Godwin's wonderful guidance, wherever, there we go, into becoming a general-purpose application interface. An alternative to Whiskey, an async alternative to Whiskey. An async alternative to Whiskey that also handles um WebSockets that is also able to deal with that and that is also a more general purpose interface and that is more adaptable. So um this is how the interface for ASGI

17:04

looks, uh as of ASGI 3, which is we're done now. I mean not we're done but we're we're kinda done for the the big stuff anyway. We've got these three variables that we call into the function with scope, receive, send. Scope is a whole bunch of state information about the incoming connection in a dictionary with a whole bunch of keys in it And receive and send are two channels on which the web application communicates with the web server. And you use, for example, in the HTTP context, you'll use the receive channel to do things like pulling the body

17:49

of the HTTP request. You don't want it in the scope because then you'd have to have the whole body arrive all at once. So instead you'd like to be able to stream it in in case you need to do that. And also for sending out the outgoing HTTP response. Um Askey gives us a lot of a lot of nice things. So obviously we've got the Potential for the performance characteristics of async that we've talked about. Real-time communication. So it's not just WebSockets, there's also service and events Which are very similar to WebSockets, but they're over HTTP only, and they are unidirectional, so just sending from the server to the clients

18:39

But they can be a nice simple thing to use without having to go the WebSocket routes. Uh HTTP long polling. HTTP2 server push. where when a request is made to your web server, you've hit the home page, the web server knows, okay, I haven't got any cookies from this thing. It probably is going to need all of these other assets on the page and I don't want it to have to go and do a couple of different round trips. before it gets them, so I'm going to preemptively start pushing these assets over the connection. I'll send back there, here's the home page, and I'm also going to start sending down my cat gifts at the same time. ASCII also

19:25

has um startup and shutdown events, which gives a more nicely managed context. for running tasks within within that domain than Whiskey has, which doesn't have any kind of uh way of communicating, okay, I'm ready to go now, or okay, can you please start shutting things down? And actually it's pretty powerful because it allows us to do things like build uh clock or timer driven events and know that the events that we're scheduling, if they're running, then when the server requests Uh when we're requested to shut down, we can wait until those events are finished and then we can send back to the server say, okay, I'm all finished now and we know that we're going to terminate cleanly.

20:15

Which can, you know, potentially allow us to build really nice task queues and so on in a much more simple way. And it's a more adaptable interface, right? We can potentially extend this into other protocols as well. So it's here for the long term. Where's the ASCII landscape at the moment? Okay, so uh we've got several different server implementations already. We've got Daphne, the original one, uh Hypcorn Somebody else uh Phil Jones has been working on and UVCorm is one that I've spent my time on. And we've also got lots and lots of ASCII web frameworks emerging. Starlight is the one that I've been working on and I'm going to show you some of how that looks a bit different to Django and why that's interesting in a moment

21:02

Django channels is slightly the odd one out on this list because that's async on the on the front but threaded most of the rest of the way through. And we've also got other stuff that's starting to be developed in this area as well. So let's what I what I want to do over the next few slides is take a look at If we're building an async web framework, what are some of the ways that we could do things a little bit differently? And I think a lot of this can feed into some of the work that we're hoping to start doing on Django over the coming months. Now, Starla, okay, up here this example. Looks like any old standard micro web framework, but there are a few ways that it's put together that I think are a little bit interesting

21:51

Now, it's one thing that we're gonna see a lot. It's ASGI all the way through. We use the ASGI interface as the primary thing on which the stack is built, all the way right up to the when you when you're working with a view and then you're in request response and you're dealing with requests and responses but all the way through uh every other part of the system if you start to dig into what's happening It's based on ASCII. And here's a good example of that is a response install it itself exposes the ASCII interface So a an instance of a response is a valid web framework.

22:37

It's a very small one that just does one thing, but it is. Okay, interesting. Okay. Just to get a bit of an idea about how some of the components look, this isn't the level that you would be working at normally. This is using requests and responses within a raw ASGI interface. Normally you'd be working within a proper request response view, but You can see how requests are just an interface that is instantiated over the ASCII state that you can then do stuff with. Okay, fine. Let's have another look at ways in which we use ASGI all the way through. The test

23:22

client. The test client in Starlet is built upon requests. It is requests. It's requests but with an as uh an adapter class, an adapter class that instead of making raw network requests Plugs directly into AN ANNE into an ASGI framework. And that's great because We can use our test clients to test any ASCII web framework, not just Startup, but any of the other ones that we saw were up on the screen earlier, or to test any.

24:07

Micro, you know, any ASCII component as well. So here we're just instantiating a response and we're making a request out to it using the standard request. library with all of the standard API and behavior as requests. The next place We said we're using it all the way through, so of course we're writing ASCII middleware as well. Now in say Django and lots of other web frameworks What happens with the HTTP dispatching is that the first thing that happens, the request comes in, we create some kind of request instance. And then we pass our request instance all the way through a middleware stack that gets request instances, calls into the next thing in the chain, and returns response instances.

25:00

Why why why why wouldn't we want to do that? Sounds fine, sounds good. Well What's nice about using ASCII as the middleware interface is that your middleware implementations Are reusable across again any ASGI web framework. They're also independently testable using the same test clients that you use for testing everything else. It's also important to design things in this way because if you build your middleware as a request-response interface, what do you do when you come to WebSockets? We're gonna build another different type of interface.

25:49

Uh if you if you're working against the YASCI interface, it's much clearer how to build For example, middleware that authenticates both HTTP and WebSocket requests based on the headers in either. Okay, but as a developer you don't necessarily want to be working at that level or time. No problem. One of the things that Starlet provides is a class that you can subclass. That to you, the end developer, provides a request response interface, but to the outside world provides the ASCII interface. So when when the request comes in it creates a request instance, sends it off to your dispatch

26:38

methods. You call into call next and it does the job of inspecting any messages come coming back down and marshalling those into response instances. And that's great because you're working at the request response level, but again, you're building reusable middleware that we can share with the rest of the community and that when the rest of the community are building middleware they can share with us. What else? Mountable apps. Top example here. If you want to build a file server in Starlet. That's what you do. You can run that top example with Daphne, with HyperCom, with UVicom and um

27:23

And that'll just work. If you want to put that within your web application and serve static files within your web application, you mount That to a particular endpoint. And again, we're building more and more reusable components. So great example of this Um, you know, talking half an hour ago about Ariadne GraphQL server, it so it has an ASGI interface. You can just plug it straight in and it's not coupled to a particular web framework. It's about ASCII. Another example, class-based views. Similar sort of thing. The class-based views install at ExposiasCI interface. You can do interesting things like it, you know, that allows you to build out

28:13

more high-level um variations on this. So if you want to build something that's like REST Frameworks view sets or anything else like that. One of the other things that, and we'll see it again a bit later on, is a point of difference is All the way through in this style of design, we're using per component configuration. So we're not using uh framework level settings that are then action over distance picked up somewhere inside the web framework, not quite sure where, and have some kind of effect. Hard to see as a developer if you want to kind of plumb

28:59

into what's happening here, where where is this getting used? And we're also not using application-wide settings where we're plugging all of the configuration directly into this one single application instance. So per component configuration, which again um helps with better reuse shareable components across the ecosystem, helps with being able to test components in isolation. So on. So what I think is interesting about some of the design in this is again, you know, all stuff that I hope as we're progressing on Django can feed into some of the ASCII work that's going on there. And it's all about the overall complexity of the stack.

29:45

You've got this one single interface style that runs all the way through, very consistent. You can use a test client with lots of different components. It's very, very composable. It's also very performant because we're not introducing any extra abstractions on top of this interface style. Whee, Blimey. Oh yeah, I said that I say that. We're not in the States, so nobody picks me up for it, but there we go. Um Okay, async. Great. What about the database? Blimey. We don't oh god there it is again. Um Okay. Django

30:30

Rm, SQL Alchemy are both thread synchronous APIs. And not just that, but if you go down to the lower levels, there's something that is kind of very analogous to WISGI in a way, is the Python interface that is used to separate the database driver from the higher level code that is working working with that driver. is also a thread synchronous interface. And we've got lots and lots of folks who've been working in the async space developing async database drivers But we don't have a standard interface onto them. So it's difficult to start writing tooling that works together with SQL Lite

31:17

and Postgres and MySQL. So I've recently released a package called Databases, which aims to address this. It's not uh analogous to DBAPI exactly because it's a slightly different level of interface It's aimed to be something that is uh you know a very developer-facing interface. You can use it to make raw SQL queries to any of those async database drivers You can also use it to work together with SQL Alchemy Core, so the table definitions and the query builder. Which is an absolutely stellatal. It's not all the way to an RM, but it's a really productive level to be working at nonetheless.

32:05

It's also great because Because it allows you to use SQL Alchemy Core, if you write your table definitions using that, you then have support for migrations using their alembic. which is you know analogous to Django's migrations, and it provides transaction support as well. and dealing with database connections sensibly handles all that sort of stuff for you. Great, so there's a low-level answer to what do we need to do in the async land in auto. Address the database. But there's there's still a component that's missing there, which is a fully fledged ORM.

32:51

So I've also released another package independent of databases but built on top of it, which is a Django-like or the start of a Django-like ORM, but asynchronous. And again, because it's built on top of databases, we've still got the migration support. It's got a very Django-like API. It's not all the way there yet, but we've got a bunch of stuff in. So we've got all of the different filter expressions and so on. We've got support for foreign key relationships and we've got support for select related. We don't have support for many to many yet or reverse foreign keys or prefect

33:37

related. And if you're building an async ORM, because I. O. always needs to be explicit, there are a few things that we need to do a little bit differently. Um so for example in the Django RM, if you've fetched a model instance You haven't fetched a relationship on it and you access that relationship on it, it will go off and generate some SQL and resolve that automatically for you. Can't do that with a with um async. You have to either call have called select related on it in the first place or explicitly load it.

34:24

and say I want to resolve this thing now and it will raise an error to you otherwise. Um Similar stuff with paging through query sets. If you don't want to fetch the entire set of results in a query set all at once, you need to do that explicitly. And the syntax is It's an async iterator, so it looks like async for instance in query sets. But it's a bit different to being able to just do that implicitly in Django. Um as well as looking a bit differently, there are also benefits to this explicit style. So I think a very large number of people in this room.

35:09

will have hit the types of cases where maybe you're working with your own code base or you're working with a code base that you've recently come into and you go, this view is running really, really slowly. Why is that? And you dig into it, you dig into it, and you find that somewhere in the template code it's iterating over a query set and it's accessing some relationship or some field on there that's not there, and it's generating SQL queries. again and again and again. Now in async in a in a standard star at setup You can't run database queries in a template at all. It it stops you from doing that. It ensures

35:54

that anything that you're accessing you have loaded. And if you try to load something without explicitly doing that, it will raise you a a great big error. Similarly, you can't do database lookups in Stra. And you know, I've worked with clients who go, this thing is this bit in the admin is running incredibly slowly. Why is that? It's because you're You know, when you're displaying your model instance, you're generating SQL queries. You don't want to do that. And Having that tighter control, yeah, it's a bit more to think about, but it's also huge benefits as well. You know, people run into this with Django and they go, Django's slow.

36:40

Well it's not, no, but it's allowing you to not look after yourself. Okay, now this is a bit different, okay? But I've been working with it for a while and I really, really like it now. I really like it Uh what else? Yeah, there's even more to think about. So if you've gone to all this trouble of building potentially very high concurrency services, you might want to be precise about how you think about Database connections and database transactions. So a really good example of this is if you are building microservices. You've got a gateway API, and the job of the gateway API is to request comes in

37:26

Make a database access in order to authenticate the user. Great user's authenticated. Go and send an HTTP request out to some other service. Wait for the response Send it back to the end user. And what you don't want to do, um, if you want to be able to build very high throughput services like that. is hold on to your database connection or your especially not a database transaction for the entire duration of that HTTP request. There's just no need you want to acquire your connection Do your work, let go of it, then go and make your HTTP request because that is a really, really slow operation. You don't want to be hanging on to this valuable system resource for the whole duration of it

38:13

So for example, databases is designed to be very liberal with acquiring and releasing connections to the connection pool unless you are explicitly within a transaction. And there's other places to think about as well. So if we want to have async HTTP requests, the requests library doesn't give that to us out of the box yet. Okay, um I've just released a package recently, requests async, which is requests with a different adapter that makes asynchronous network requests. Email, really you want to make non-blocking SMTP requests. There's a great library for that already. Caching, you're you know

38:58

interacting with Redis or Memcache. Again, that's a network operation. I thought it was a back of the stage there. Even validation, right? So you're building a validation like reform validation, API validation. If you want to be able to perform any database queries within that, you're going to need to be able to make sure that your validation library provides uh support for async methods as well. And stuff like password hashing. So password hashing is interesting because it's deliberately designed to run slowly. And if you run something very, very, very, very slowly in uh a single async task

39:45

Then what happens is all of that normal very fast interleaving between all of your different tasks stops happening and this one task is just hogging everything else up and these other tasks are getting blocked So uh two different ways you can resolve that. Either you can make sure that your password hashing is yielding to the other tasks. Lots of the ways through. Or you can take a simple approach, you just dispatch it off to a thread. You say, okay, go and run this thing in a thread. Don't block the main event loop. So, this is the stack of stuff that I've been working on recently and trying to lay a really healthy um kind of groundwork for all of the async

40:30

stuff that I think is about to be on the way at a very great rate. And let's take a look at how all of this stuff hangs together if you want to start building full stack web frameworks based on this. Don't worry if you can't read everything in the slide here. It's not so bad, right? But it it's just to give a bit of a flavor of a few different Points of difference between what we're used to in Django and some of the design style here. Settings, we're still pulling all of our settings together in the same place. We're still using standard 12-factor configuration style. But we're then using those settings and pulling them into the components or the resources that we're using them within so they're less entangled

41:23

in their design. Um models kinda similar. Point here being the async landscape is really starting to mature. Type validation. Type system is a library that handles both API validation and form rendering, kind of similar to REST framework serializers, but it's trimmed down a bit. The difference here being our validation libraries need to be able to support async methods as well. StarPoint, in Starlet we call them endpoints or in RESTRAM work included in Django views. The interesting thing to kind of note here is it's very, very clear where our database access is are happening or

42:13

other or network requests. So we've got some views which are not async. There's no database accesses that are happening in there. They don't need to be async. And we can see exactly where stuff's happening with the database, which is really great. Routing, pretty similar. We've talked about per component configuration and being able to mount ASGI apps. And pulling it all together, we've got a single app instance at the top, which ends up just being A bunch of middleware that you pulled into it, a couple of other default middlewares that it will always include because you pretty much always want them that deal with server errors or exception handling

43:00

Uh so the middleware and then the routing. And that's it. So we've got something that's quite Django-ish. This is very, very decoupled, uh low, low impact approach. Um performs great in terms of throughput As great as you could reasonably need. It's able to support WebSockets or Service Enter events. And various other stuff, you know, background tasks we mentioned briefly, and HTTP2 server push. And It lets us do things that we just can't do with Django at the moment, right?

43:49

So uh building API gateway services that can comfortably handle uh tens of thousands of requests per second, building proxy services, um building GraphQL backends with real-time subscription endpoints that can serve thousands of clients at the same time. Uh all things that we would really like to start to be able to push Django into the realm of and doing all the legwork for this. And the other thing to you th that we have is this very very composed style. So we can start right at the bottom here, uh a raw ASCII interface. We can work our way gradually up

44:34

using individual ASGI components up to using these tools in a very um micro-frameworkish way all the way up to using them in the same sort of way as a full-stack web framework So what does this mean for Janko? Um a few things in tandem. I suppose it should be two things in tandem, really. I don't know what a three-way bike's called. Um First of all, progressively adding ASGIN to the stack. So Andrew Goldwyn has what I think is actually a very achievable proposal for how we would go about

45:21

Getting some aspects of this into Django. Starting off by adding an ASGI interface onto Django but running everything within ThreadPools beyond that. Then gradually you iterate and you build that out so that the middleware stack is ASGI based and at that point if you need to be able to drop into async in a view then you can Your Django components might not necessarily support it, but you've you know if you want to do a little bit if you don't mind doing a little bit of extra work you've got more power available to you and then iterating on from there and looking at turning some of the components async along the way. And the other things say, you know, there's a lot of groundwork that's being done for this already.

46:07

Things like the databases package could potentially be used in an async Django RM. Maybe we don't need an async Django RM. and maybe a one that just dispatches into the thread pool will work equally well. But um we've got a lot of groundwork layers that Points. And at the same time as all of that, pushing really hard to keep maturing the async landscape. And in particular, pushing really hard to do that in a way That is something that the whole community can share. So getting behind ASCII is a standard because it allows us to work together really efficiently. And I think that the Python community has

46:55

a really important message here, which is, you know. I don't think there's anything out there that beats Python for productivity. It's absolutely awesome. And async makes it competitive with the other big players in its zone with Node and with Go. And bring support for real-time protocols and loads of other stuff. You know, to me, if we can Reach a great level of functionality with this stuff, Python's really hitting the sweet spot there.

47:41

Yeah. I think it's a re it's a really simple message and I think it's really powerful and I think it's one that we st need to start communicating. um to the rest of the world. Okay, um quickly, um all of the Django Fellows work only ever happens because it's sponsored Uh my time only happens because it's sponsored. I think that um The the ask that we have when we say, hey, I want to work for your companies full-time for 50 euros a month. or 150 euros a month, you know, if your businesses can see the potential of the impact that this sort of work plus the work

48:29

on rest framework, the 3. 10 that's coming out soon and the open API support that's going to be in there and your companies are going to benefit from these things over the next year, the next two years, the next five years, and into the long term. I think the investment is just an absolute no-brainer. So whether that is through the DSF, through REST Framework sponsorships, whether it's through community events like this or Django Girls or local events in your something, anything. It's so important. Uh I all I also think in order for us to Take things to the next level

49:15

that we're going to need to find other monetization strategies as well, and ones that benefit the community. And I think for this to happen with frameworks like Django and like Rails. They need to be truly succeeding in the product space as well. Providing uh great products that are the fastest possible on-ramp for developers to get started with Django or with Rails. Give developers the ability to start mocking out your API, your API documentation, or give developers the ability to start sketching out their admin

50:02

and start putting data in there Yes, you might be limited to a certain number of rows in there. We've got some constraints that we're not going to do it, you know, we're only gonna be with you for the prototyping stage. But the goal of our product is to lose you as a user. You we want you to end up taking complete control of the framework at the end of the day. We'll have a great big button there. Great, let's get started on my product. I'm um happy with the prototyping stage, our front-end team's been working against it. And you know, at the end of the day that is the most efficient thing for us to be doing for our developers at a certain point in the maturity of these frameworks. And you know, if we can do it, if we can nail it, we could have

50:49

we could have a company that just works on Django or Starlet or the Python ecosystem full-time and you know the more we can suc we can succeed with this, the more developers we can bring on board. I was chatting over the slides with Carlton late last night and we went over these last three slides. And Summed summed them up to him at the end and said First one is give us your money. Next one is here's how we make money and the last one is it's not about the money Right? And it's not.

51:35

It's really not. You know, the money's a tool. Okay, why are we here? Why do we care about open source? Right? Why have we all come together? Our guiding lights in all of this are thing that keeps us focused has to be, you know, impact on society, betterment of society. There's this great quote by uh this uh an American Trappist monk Thomas Moore which is you know we m if we do not do this we may find ourselves climbing to the very top of the ladder of success only to find that the ladder has been leaning against the wrong wall.

52:21

Okay? And If we focus on our values rather than allowing the raw power of the market to set the direction and the flow Then we change the rules of the game and you help evolve the very currency with which the market actually trades in. We oh hang on a minute, I didn't show you my lovely bit that was the slide of that one. Uh you know, it's all about Taking our individual creative spark, our individual

53:07

instinctive sense of empathy and working together as a community, as a whole and as a society. Thank you.

Questions this talk answers

What is async concurrency in Python, and how is it different from threading?

Async uses multiple tasks running in a single thread, with context switching managed by the runtime at explicitly marked I/O points. Threaded concurrency instead uses multiple threads whose switching is handled by the operating system.

Discussed at 5:27

What are the main trade-offs of adopting async Python?

Async can provide much higher concurrency and efficient non-blocking I/O, but it requires rewriting or replacing low-level libraries, learning a distinct programming model, and explicitly managing the boundary between async and synchronous code. It may also offer little practical benefit when throughput is not a concern, since latency is a separate issue.

Discussed at 7:49

Why does Django need ASGI instead of WSGI for async applications and WebSockets?

WSGI is fundamentally a thread-based interface designed for ordinary HTTP request and response handling, so it cannot support async context switching or WebSockets cleanly. ASGI is its asynchronous, more general replacement, supporting WebSockets and other protocols as well as HTTP.

Discussed at 15:31

What can ASGI support besides ordinary HTTP requests?

ASGI enables WebSockets, server-sent events, HTTP long polling, HTTP/2 server push, and startup and shutdown events. Its lifecycle support also makes it easier to run cleanly managed background or scheduled tasks.

Discussed at 17:49

How does an async ORM differ from Django's ORM?

Because database I/O must be explicit in async code, relationships and query results must be preloaded or explicitly fetched, and queryset iteration uses an async iterator. This prevents hidden database queries in templates or attribute access, making performance problems easier to detect at the cost of more explicit code.

Discussed at 33:37

How could Django gradually add async support?

The proposed path is to add an ASGI interface first while running existing Django code in thread pools, then make the middleware stack ASGI-based and gradually convert individual components. This would let developers use async views and capabilities incrementally without requiring the entire Django ecosystem to become async at once.

Discussed at 44:34

Presenters

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos by Tom Christie

More videos from DjangoCon Europe