The request response cycle a Djangonautic journey | Timothy McCurrach

This video features Timothy McCurrach at DjangoCon Europe 2021 in Online.

The request response cycle a Djangonautic journey | Timothy McCurrach
0:39:14
Published June 27, 2021
1,648 views

How exactly do web-servers communicate with Django? and what happens immediately after that? If you've ever wondered where the request object comes from? How the middleware-chain is put together? Or just what all those functions you see in your exception tracebacks do? Then join me in a deep dive of everything between the server, the view and back again, as we answer these questions and more.

This talk will be a look under the hood at Django's request handlers, middleware-chain and URL-resolvers. Focusing on just the WSGI route (not asynchronous), it aims to be accessible to beginners, but with the intention that a more experienced user will learn something new or interesting as well.

We will start off by replacing Django's WSGIHandler with our own "hello world" WSGI application. We'll then build back in additional features until it starts to resemble what we originally had. Along the way, we'll highlight lesser-known features, and give examples demonstrating how understanding the implementation will enable you to better take advantage of the features Django offers, and ultimately write better code.

Summary

Timothy McCurrach reconstructs Django’s WSGI request-response path from first principles, showing how a WSGI callable receives the server environment, creates a request, resolves a URL, calls a view, and returns a response. He incrementally builds a simplified Django handler with response status and headers, cookies, signals, middleware, URL routing, startup validation, exception handling, view and template-response middleware, and atomic requests. He argues that understanding these internals makes Django’s abstractions less magical and explains why middleware is built and ordered as it is; he notes that the real implementation also contains checks, logging, and async-related code.

Key takeaways

  • A WSGI application is a callable receiving an environment dictionary and a callback, then returning an iterable of byte strings after setting status and headers.
  • Django’s handler turns raw WSGI data into a request object and passes a response object back through the server with status, headers, cookies, and content.
  • Middleware wraps the request and response flow, is assembled once at startup, and can short-circuit requests or process views, exceptions, and template responses.
  • Django resolves URLs through a resolver selected from the request or the root URL configuration, then attaches the resolver match to the request.
  • Exception handling wraps each middleware layer rather than only the whole chain so middleware that starts processing also gets a chance to finish processing.
  • Atomic requests wrap the view in a database transaction, not necessarily every part of the request lifecycle.

Summarised automatically from the transcript.

Transcript

5,870 words · auto-generated Show

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

0:09

Speaker 1: Hello, my name is Tim McCurrick and I'm going to be talking to you about what happens when a request comes along and Django needs to deal with that. Now, when we talk about the request response cycle, we could talk about what happens in the browser or the network layer and so on. But for this talk, I'm just going to restrict myself to talking about what happens within Django itself. Furthermore Django has two means primarily of communicating with a server. There's WISGI, which I'll talk about in a moment. And since Django 2. 2, there's also been ASGI, which added support for various asynchronous features. But in this talk we're just going to talk about the WISGI route. So

0:54

Speaker 1: what is WISGI? Well it stands for the Web Standard Gateway Interface And it's just a specification. So it's not a server or a framework, which is a common misconception. It's just a set of rules which says you've got a server which adheres to these rules. And if you've got a Python application which adheres to some other rules, well, that Python application is going to run on that server. It's all laid out in PEP 3333 and you can go and read the full details there. So what does our Python application need to look like to be Whiskey compatible? Well, it needs to be a callable and a callable that accepts two arguments. That first argument is going to be a Python dictionary.

1:42

Speaker 1: So when our server gets a web request, it's going to call this function. and it's going to pass in a dictionary containing all of the information you would associate with that request. So there'll be a method. a scheme, some URL information, some query parameters perhaps, and also some additional information about how the server's configured. The second argument is a callback function that I'll talk about in a moment. But beyond that, it's just a callable. You can do anything you like in there so long as you can write it in Python code. Once you've done what you want your Whiskey application to do, there are two more things that the Whiskey spec requires.

2:27

Speaker 1: Before you return anything, you need to call this callback function that was passed in as the second argument. And we do two things here. First, we set the status of the response that's going to be sent back to the client. And second, we set the response headers. So that second argument there is going to be a list of tuples with name value pairs. Once we've done that, we're free to return our content and we must return an iterable that yields byte strings. So you might have wondered why are we returning a list there and not just returning response as it is And the reason is because we want to return an iterable. Now, a byte string actually already is an iterable, but if we were to do that, the server would iterate through it.

3:14

Speaker 1: and would send back the data one character at a time to the client side. That's obviously not something we want to do. So that's a whiskey application, and Django is amongst other things an example of a whiskey application. So how does Django implement this? Well, when you start a new Django project, you'll have a folder structure, something like this, and you'll have this file whiskey. py that you probably ignore most of the time. But if you look inside, down the bottom we have this line application equals get whiskey application. So when your server starts up, it's going to import this. and that getWSGI application function is going to run. So let's have a look at what that does. Well it returns Whiskey handler.

4:01

Speaker 1: Notice we're returning a instance of the Whiskey handler class. And that class instance is the callable So that's the gateway from server to everything else that happens in Django. So all of those things all start with the core method inside this whiskey handler instance. Now, now we know how to make a whiskey application. We could if we wanted to replace whiskey handler with our own whiskey application. There wouldn't be much point in doing that because then you would forgo all the features that Django gives you for free. But in this talk, we are going to write our own whiskey handler. And the idea is we'll build it back up incrementally until we have something similar to what Django gives us in the first place.

4:48

Speaker 1: That way we'll understand all of the things that Django is doing for us when it handles a request. So here's our first whiskey handler. We've implemented a call method so that it's a callable. And the rest of the code here is just the same Hello World WISGI application we had a few slides prior. Now obviously we want to make it a bit better than this. And the first thing we're going to want to do is return more than just hello world. Django is a web framework, so we want Django users to be able to write their own views. We don't have any URL routing yet, so I've got a setting which I'm calling the View, and we're going to import just a function from there and call it. So now already with just these six lines of code

5:34

Speaker 1: we can actually do quite a lot. We can return some HTML, we could serve up a static page. We have access to things like the ORM, so we could keep track of how many times the page has been viewed, but it's still reasonably limited. We couldn't, for example, have a form on our page because we would need to respond to get and post methods differently. And at the moment we're not passing any of that information forward into the view. We're just calling it So one thing that we could do is we could pass in the addiction we get from the server, and then the view would be able to say, okay, this is a GET request or a POST request, and it would be able to respond accordingly. However, lots of the information in that dictionary is quite raw and it would require some decoding and processing that doesn't really belong in the view layer.

6:25

Speaker 1: Of course, we all know the solution to this, which is that every single Django view has a request as its first argument. and it's in the uh whiskey handler that this request object is created and passed into the view. Now I'm not going to go into lots of detail about how the request object is implemented. That's a talk in and of itself. But just so that we can see there's no magic going on here. The idea is we instantiate it with a dictionary and then we extract out all the pertinent methods, all the pertinent information, and put it in a nice format. So we have these properties for things like query parameters and cookies, and then we have a nice API that we can use

7:10

Speaker 1: in the view to get all of that information in a nice format. So here's our whiskey handler and we create a request and we pass it to the view. Django actually implements it slightly differently to that. it uh sets the request class as a class attribute on the WSGI handler. And this is one of those details that actually it's really useful to know about because it means if you wanted to subclass request and maybe add your own methods or tweak the behavior slightly, you could do so and by setting this a class attribute or use the subclass request. So here's our updated whiskey handler and now we can deal with forms. We could use that request object to do some basic URL routing in the view layer if we so wished

7:58

Speaker 1: But there's still some limitations. We couldn't, for example, return a permission-denied response. Why? Well, if we look here, the status is hard-coded. as are our response headers, so we wouldn't be able to do things with cookies, for example. All of this information needs to come from the response. And if you've written a Django view, you'll know what the solution here is. We have this response object that encapsulates all of that information As with the request, I'm not going to go through it in lots of detail, but just enough so that we can understand what's going on in the handler. So we instantiate our response object with some content. And you might be forgiven for thinking, well, we can just return response dot content like so.

8:44

Speaker 1: It's actually a lot more flexible if we make the response object itself the iterable that we return. So how does this work? When we set our content, we store it in a private container attribute, and then we implement this iter method. to turn the response object into an iterable. There's a bit more going on there than that, but that's the essence of it. So we still need to deal with this issue of the status. So when we instantiate our response object, we can pass in a status code. And uh Django also has this nice method which gives us the appropriate phrase that goes with that status code. So you know if you pass in 404, it will return not found.

9:30

Speaker 1: And so now in the handler we can say well response. status code, response. reason phrase, and and we get that. And likewise we need to deal with the response headers. And the idea here is by implementing these setItem and getItem methods. we turn the response into a dictionary-like object for storing response headers. So if I wanted to set, for example, the content type response header, I would just write response. Content type equals text plane. We also have a separate API for dealing with cookies which does a lot of things automatically for us. And so in our handler we list those response headers separately.

10:16

Speaker 1: So now we have a handler that can do most of the things that we want it to do. But Django adds a couple of features that it's worth knowing about Before it does anything else, it dispatches a request started signal. And so we can add that in. And Django uses this internally for things like database connections, but it's actually quite useful for testing as well. So it's worth knowing that it does this. And it would be good to have a request finished signal to go alongside that. And the Whiskey spec actually specifies something that makes it easy to implement this. It says if the iterable returned by the application has a closed method, the server or gateway must call that method upon completion of the current request.

11:01

Speaker 1: So we can take our response object and we can add a close method, and it means the very last thing that happens After the server sent the response back to the client, this request finished signal will be dispatched. Notice we need to provide a sender and self in this case refers to the response object. So we'd better set a handler class on the response. So if we go back to handler, we can say response. handler class equals self. class. And now we have something that's actually pretty similar to what Django actually implements. One big difference though is that we are still only returning a single view

11:48

Speaker 1: and we didn't have any middleware or anything like that. Now I'm going to move that logic into its own function that we can then build out to add some middleware and some URL routing So an introduction to middleware, the idea here is that our request is going to propagate through the middleware reach our view and then the response will travel backwards through the same middleware before finally being returned. How does this work? Well, our middleware wraps the next piece of middleware along, and then when we call it, we can do something useful. Then we pass the request on to the next piece of middleware and so on. And eventually we get a response back, which we can process and then pass back again.

12:38

Speaker 1: Of course, in the forward direction, as well as passing the request on to the next piece of middleware, we could also return it early, in which case it's never going to reach the V and the response will just travel backwards. So how are we going to implement this? Well at the moment the function that we call to get our response is the view. Now instead of calling that we're going to build a new function that's going to be the view wrapped by all of our middleware. And this function we're naming handler and let's see how it's built. Well we start off with handler just equaling view And then we loop through each piece of middleware, import

13:25

Speaker 1: it, and wrap the handler. So before our first iteration, handler is just for you. After our first iteration, handler is going to be view wrapped by a piece of middleware. And then with each subsequent iteration, it gets wrapped again and again. Notice that we loop through our middleware in reverse order so that the first piece of middleware ends up on the outside. Now this whole block of code here where we build this function that we eventually call doesn't need to happen on every single request. The middleware doesn't change, so we can do it just once, and that's a lot more efficient. There's actually a more important reason to do it upfront, which is that Django has a philosophy of trying to warn you about things that it can

14:17

Speaker 1: right at the beginning when the server starts up rather than waiting for a request to happen and then it to error out. So let's move all this code and put it in its own function called load middleware. And then we're going to need to save the function we've made to the middleware chain. Add this attribute middleware chain. Then in our getResponse function, instead of calling the handler, we're going to call self. middleware chain. And the only thing that it remains to do is to actually call load middleware. And so we put that in the init method. So this will happen right at the beginning. So here's what our whiskey handle looks like. Now I mentioned earlier that

15:04

Speaker 1: Django tries to warn you about things when it can. So let's add that in And after we've wrapped each piece of middleware, we'll check that handler is actually equal to something When we use functions to build our middleware instead of classes, this is a common error. So it's a useful thing for us to check right at the beginning when the server loads up. Now there's quite a lot that's gone on there, so it's useful to zoom out and see everything that's happening so far. When our whiskey server starts up, it imports whiskey. py. That's going to instantiate a whiskey handler instance. And so in the init method, we then call load middleware.

15:52

Speaker 1: load mid middleware, build self. middleware chain. And then later on when a request comes along, the server calls the call method of our whiskey handler. We make a request object and call get response. GetResponse passes that request object onto this function that we've built, middleware chain. And then we get a response back, which is passed back to the server, and then back to the client side. So now we have a whiskey handler that can deal with middleware, and that's great. But the central piece of the middleware chain handler is still essentially hard-coded to a single view, and we want to improve that.

16:39

Speaker 1: So we're going to replace handler with a function that's going to accept a request and it's going to look at the URL info in that request and route it to an appropriate view, call the view and send that response back into the middleware chain. So let's replace handler with a function that's going to do that. Now in Django, as I'm sure you're all aware The URLs are normally defined in a file called URLs. py and there'll be a setting called root urlconf that points Django towards that file So you might expect here that we're going to import that file, but that's not actually what happens. We look at the request object for a URL

17:26

Speaker 1: conf attribute. And this is really useful to know because it means we can write middleware that affects how the URL resolution process happens. We might want to have different URLs. py files for different types of users, for example. And by setting the URL conf attribute to a request in middleware, we can do that. So let's have a look line by line at what happens. Well we see if the request has such an attribute. If so, we call this setURLconf function. Now what that does is it sets a thread local variable so that all the other functions in Django that need to know which URL comp

18:11

Speaker 1: we're using will know that. And then we call this getResolver function. As the name suggests, that returns a URL resolver based off the file that we've passed in. If we don't have a request, we just get the standard URL resolver based off the file described in the settings root URL conf. So once we've got that URL resolver, we call the resolve method and that's where all the URL resolution actually happens. I'd love to go into that, but that's actually a whole nother talk. The key thing we need to know is that we get back this match object. And the

18:57

Speaker 1: match object will tell us what the arguments and keyword arguments that are extracted from the URL are. uh what the view function is that um our url has been routed to, um some other useful information like the name of the URL. And another line here that's again useful to know is that all of that information is attached to the request. So if for example you had several URLs all pointing towards a single view and in that view you wanted to know well what's the URL that has resulted in this view being called, you could use the resolver match object to find that out.

19:45

Speaker 1: This next line um works because the resolver match object implements an iter method so we can use the um tuple assignment syntax there. But we get callback, which is the actual view function, as well as our arguments and keyword arguments, and then we call that view along with the request and pass the response back into the middleware chain. Now this whole section of code here is actually implemented in a separate method called resolve request It's the same code, but it's in a separate method, so I'm just going to update that so that it looks similar to how Django implements things.

20:31

Speaker 1: And so now we have a handler that can do middleware and it can do some URL resolution as well. And that's great. But there's some additional features that Django adds in which are really helpful. So when we think about middleware, lots of middleware is used for debugging and we might want that in our development environment, but not to happen in our production environment. Or we might have some middleware that interacts with some third-party service that we're using. And we want that in the production environment, but not in the development environment. And so you end up with middleware that looks like this. In the core method, we say, well, what environment are we in?

21:17

Speaker 1: Either do something or do nothing accordingly. But it makes much more sense to just not have that middleware in your production environment and not have that middleware in your development environment depending upon what you want. And Django provides a way of doing this. When it loops through the middleware, when we're building our middleware chain. If a middleware not used exception is called, we just continue and don't add it to the chain. That means we can improve this middleware like so. By saying, well, if we don't want to use the middleware in the particular environment we're in, raise the exception and it won't get added to the middleware chain.

22:04

Speaker 1: This also means we can simplify the call method so we're not having to do that check. Now One thing that we've not dealt with so far are errors and any um handler needs to be robust and deal with errors well. Django provides us with this useful decorator called Convert Exception to Response. And you can decorate a function and if a exception happens within that function what this response for exception does is it says, well, is that a 404 exception, in which case I'm going to return a response with a nice 404 page? Or is it just a exception I don't know anything about, in which case I'm going to return a

22:52

Speaker 1: 500 page and so on And so you might think what we could do is we could wrap the entire middleware chain in this convert exception to response. So if an error happens anywhere up and down the chain, it's caught. But let's think about the implications of that for a bit Supposing we've got our request and it's working its way through our middleware to our view where an exception occurs. The error gets picked up by convert exception to response and a nice response is sent back. But notice none of the middleware was called on the way back. And this might be bad for several reasons. There might be headers that are set that are required for the browser to even accept the response.

23:42

Speaker 1: From the perspective of someone writing middleware, it's also much simpler if you know that if the first part of your middleware is going to run, then the second part of your middleware is also going to run and it allows you to write robust middleware a lot more easily. So instead of wrapping the entire middleware chain A better idea would be to wrap each piece of middleware. So as we build the chain We wrap each piece of middleware in turn and we're also going to want to wrap the central piece, the get response function. And so this way, it doesn't matter where the exception is raised, we know that if the first part of some middleware has run, the second part of that same middleware will also run.

24:32

Speaker 1: So now we have some slightly better exception handling. Now one thing we might like to do when we're writing middleware is we might like to know in advance what the view is that's about to be called. But when we're working our way through the middleware on the way in, there's no way we can know this because the URL resolution hasn't happened yet. The solution to this is view middleware. And the way this works is back at the beginning when we're starting up our server and we build the middleware chain, we check each piece of middleware for a process view method. If it has such a method, we add it to the view middleware list. Then later on when we're about to call our view, Before we do so, we loop through each of these pieces of view middleware.

25:22

Speaker 1: Crucially, at this point we now know what the view will be, so we pass that in and we can use that information. We don't expect the view middleware to return a response. If it does, we use that response to pass back down the middleware chain instead of the view. But assuming there is no response returned from each of these view middlewares, we continue on, call our view, and then continue as normal. There are two other special methods that we can add to our middleware. One is exception middleware. So this works in a similar way in so much as Back when we're building our middleware chain, we check each piece of middleware for a process exception method.

26:10

Speaker 1: If it has it, we add it to an exception middleware list. And what the exception middleware does is When an exception happens specifically inside the view, we loop through the exception middleware to see if it can handle it. If it does and it returns a response, well that's the response that gets passed back down the middleware chain. Otherwise, we re-raise it and it gets handled by the standard exception handling we talked about earlier. This code actually belongs in its own method, so I'll just change that there. The final special method that we can add to our middleware is template response middleware.

26:57

Speaker 1: So again, when we're building our Middleware chain, we check for the process template response method and we create a list of all our template response methods And this is this mid middleware gets called after we've called the view. Now Some responses can have a special render method and we use this for delaying the template rendering So once we've got our response, we loop through each of our template response middleware and we expect them to return a response because it needs to get passed on to the next piece of template response middleware that's going to run. Once we've looped through all of those, then we call our render method, which is probably going to render a template, and then that's what gets passed back down the

27:52

Speaker 1: middleware chain. Here we also catch exceptions and if there is an exception that also gets handled by our exception middleware Now there's quite a lot of things going on here, so a picture is helpful to see all of this. Our request travels through the middleware Then the URL resolution happens, so we have a view to call, but before we do that we go through the view middleware, call our view, any exception middleware that needs to be called due to an exception happens any template middleware that may or may not be called happens and then we go back down um through the middleware again um back to the server. So one more thing that I think it's worth mentioning is that

28:40

Speaker 1: Django has a database setting called Atomic Requests. And what that setting does is it wraps each view in a database transaction. So we can do that here. We've got a method makeView atomic, which I'm not going to go into detail about, but if that setting is on for a particular database, it will wrap the view in a transaction. Notice that even though the setting is called atomic requests, it's just the view, not the rest of the request that is wrapped in a transaction. So now we've built up most of the core request handling that Django provides for us. It's not exactly the same. There are some bits that I've missed out, some things like checks and logging. and some more significant things like um

29:27

Speaker 1: stuff to deal with asynchronous functions. But it's mostly all there So that's it. I hope you found it some combination of interesting and or useful. And thank you for listening. Saying Emma would like to speak. I'm not sure if there's anything I need to do.

29:49

Speaker 2: Just call me and I will answer. Uh so um I was wondering as a uh middleware writer, as you mentioned before, is there any other entry point into middleware that you would like to exist And that does not exist yet, uh, in Django.

30:12

Speaker 1: Do you mean any sort of additional special methods?

30:17

Speaker 2: Yes, any additional hooks like uh Uh I don't know, first uh first response or something like that.

30:25

Speaker 1: Hmm. That's a great question. And The answer is I don't really know. I'd not I'd not thought of what what else I would like to see added. Um Sorry, nothing immediately comes to mind. But um I mean did you have any um ideas of something that you'd like added

30:49

Speaker 2: Well I I'm asking because uh I don't know if you if you saw my talk, but uh I did some uh some free writes of uh Django. Um and so I went also through the the request response cycle and I I did my middleware handling a bit differently. Um so for example I made sure that um Uh that a response object is always passed back uh to the middleware. Um uh response with a render uh method uh but that uh a middleware can always act on the response before uh

31:34

Speaker 2: uh acting on the render response so that the middleware can always icon a response object. And yeah, it's um it was interesting to to look into into the the middleware, the whole middleware handling. in Django and uh since I did it a bit differently I I wondered if other people uh might have had uh this similar want or need uh to handle things differently.

32:07

Speaker 1: Yeah, it's a it's a good question. Um I'm sorry I've not really got too much to to add, but it's definitely something I'd I'll think about. Um maybe one for discussion in the Slack channels. Thank you for the question.

32:23

Speaker 2: You're welcome.

32:26

Speaker 3: Hello Tim. Uh can you hear me?

32:28

Speaker 1: I can hear you, yes.

32:30

Speaker 3: So there are a lot of uh code snippets uh in your slides and they were prop they were properly marked and the diffs were all were good as well. So what how do you prepare your slides and uh prepare your core snippets to show the diffs

32:47

Speaker 1: Um so there's a um a JavaScript framework I've used called Reveal. js, which describes itself as an HTML presentation framework. So it's all written in um HTML and then Reveal. js sort of makes doing all the animations and things. uh easy. Um the the git diffs were just a little bit of CSS that's sprinkled on top I've um I've made my slides um available on the if you go to the DjangoCon site and click on the talk. There's a a link down the bottom so you can you can download and see exactly how that's done.

33:33

Speaker 1: But um yeah, reveal. js is the is what I used to make the slides basically.

33:40

Speaker 3: Okay, bye.

33:43

Speaker 1: I'll just check the Slack channel for any oh.

33:50

Speaker 4: Hi, hi team.

33:51

Speaker 1: Hello.

33:52

Speaker 4: Hello. Uh thanks for your very interesting talk.

33:56

Speaker 1: Thank you.

33:57

Speaker 4: I want to ask if you um have um uh studied or in a deep dive also in the new part of the the sheets cycle for it starts with the ASDI and and similar and if you have something you you find out uh better or worse than standard cycle Whiskey.

34:33

Speaker 1: Um the answer is no. I've not really done a deep dive into the um the ASCII handler. Um I had a very brief look at it, but I'm not sure I could uh really comment on on that. I think um So one thing that the talk doesn't really make clear is the the talk that I just did is A lot of uh the functionality that I was describing uh wasn't in Whiskey Handler. It was in a class called Base Handler, which is shared by both the whiskey handler and the um ASCII handler. So really it's just that call method

35:19

Speaker 1: that is uh part of Whiskey Handler, everything else um is shared between the two. So there's quite a lot in common uh between the two of them. I I missed out lots of um code which wraps lots of functions and kind of takes care of both the WISCI and the ASCII um side of things. So it'll be like convert sync to async and functions like that. I don't know if that answers your question. Yeah, I've I've not I've not really dived into the AC

36:00

Speaker 4: side a lot. Yeah, sure. Thanks.

36:06

Speaker 3: Um got another question. So I just wanted to ask that uh uh I've been following the same things uh that to to know what are the different parts of uh the request response cycle, but not in the framework part, in the web service part. Uh so I just wanted to know that how did you go about in discovering these? Uh did you look through the code manually just you did you just read the code or did you run some code and in the debug mode and um try to figure out which parts are being accessed and on which response and on uh which conditions.

36:47

Speaker 1: So you you broke up a bit there in the middle. So just to clarify the question, are you asking sort of how how did I go about understanding all of what's going on

36:57

Speaker 3: Yes.

36:58

Speaker 1: Yeah, okay. Um I guess uh it started I was looking at a couple of tickets uh to do with the URL routing um in the uh you know all the all the Django bugs that you can contribute with. And I just I was looking at um one of those tickets and from there I was just reading through the code um and that that I guess just you know saying okay what does that happen when does that happen and so on I think that's probably um what gave me the idea of the talk Um beyond that , when I actually wrote the talk, there are a few things that I wanted to

37:44

Speaker 1: um check. So one thing that I did that was quite useful was if there was a a line of code which I wasn't sure about why it was done exactly the way it was done, I would look at who committed it um and you can pull up the PR, read the comments, go back to the ticket, kind of read all the conversation. Um and that was actually a really uh good way of learning about why things were done the way they were done. Because you've got lots of conversation there of lots of people who know lots about Django saying, how about this? How about this? And so you can gain lots of insight by doing that. So that was that was something I learnt a lot

38:29

Speaker 1: of whilst preparing the talk.

38:35

Speaker 3: Hmm, that's nice. Thank you.

38:40

Speaker 1: Um, I'm aware that there's another talk um starting uh all about the Django Software Foundation that looks interesting. I'm happy to hang about if other people have questions, but I don't want people to miss out on other interesting talks as well.

39:00

Speaker 3: Yeah, people are dropping off, but I want to say thank you. It was a really interesting talk.

39:04

Speaker 1: Well yeah, um I'm I'm glad you enjoyed it. Thank you very much.

Questions this talk answers

What is WSGI, and what does a WSGI application need to do?

WSGI is a specification connecting a web server to a Python application, not a server or framework. The application must be a callable accepting an environment dictionary and a callback, call the callback with the response status and headers, and return an iterable of byte strings.

Discussed at 0:54

How does Django receive a WSGI request?

Django’s `wsgi.py` exposes the result of `get_wsgi_application()`, which creates a `WSGIHandler`. The handler’s callable interface is the gateway from the WSGI server into Django’s request handling code.

Discussed at 3:14

How does Django turn WSGI request data into a request object?

The WSGI handler constructs a Django request object from the raw WSGI environment, decoding and organizing data such as query parameters and cookies. It then passes that request object as the first argument to the view.

Discussed at 6:25

How does a Django response determine the status, headers, cookies, and body sent to the client?

A response object encapsulates the content, status code, reason phrase, headers, and cookies. It is itself made iterable so the handler can return it to the WSGI server, which sends its body and metadata to the client.

Discussed at 7:58

How does Django middleware process a request and response?

Middleware wraps the next middleware or the view: requests travel inward through the chain, while responses travel back outward. Middleware can also return a response early, preventing the request from reaching the view.

Discussed at 11:48

How does Django build and load its middleware chain?

Django starts with the view and wraps it with middleware in reverse configuration order so the first middleware is on the outside. It builds this chain once when the server starts, allowing configuration errors to be detected before requests arrive.

Discussed at 13:25

How does Django resolve a URL to a view?

Django obtains a URL resolver from the request’s URL configuration or the project’s `ROOT_URLCONF`, then calls `resolve()`. The resulting match supplies the view, positional and keyword arguments, and other route information, which Django attaches to the request before calling the view.

Discussed at 16:39

How does Django handle exceptions in middleware and views?

Django converts known exceptions such as 404s into suitable responses and produces a 500 response for unexpected errors. It wraps each middleware layer and the central request handler separately, ensuring that middleware which began processing can also finish processing on the way back out.

Discussed at 22:04

What special middleware hooks does Django provide?

Django supports view middleware, exception middleware, and template-response middleware. These can inspect the resolved view, handle exceptions from the view, or modify a deferred template response before it is rendered.

Discussed at 24:32

What does Django’s ATOMIC_REQUESTS setting actually wrap in a transaction?

Despite its name, `ATOMIC_REQUESTS` wraps the view in a database transaction, not the entire request. Middleware and the rest of the request-handling process are outside that transaction.

Discussed at 28:40

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 Timothy McCurrach

More videos from DjangoCon Europe