← Back to Show Notes

Transcript: Middleware

Hello and welcome to another episode of Django Chat, a weekly podcast on the Django Web Framework.

This week we're going to be talking about middleware. My name is Will Vincent and I'm

joined as always by Carlton Gibson. Hi Carlton. Hello Will. And this talk, this chat is based on

the talk you just gave at DjangoCon US, which will be up shortly and we'll link to that in the show

notes all about middleware and in many ways middleware is sort of the secret sauce of django

but i think it's poorly understood so let's maybe dive in and talk about what it is and why it's

important to django yeah i mean it's it's a source a lot of the power i think when you say understood

misunderstood i think people are a bit scared of it um because when do we yeah it does it sounds

scary like and what do we see we see we've got enough settings and we know there's this middleware

setting with this list of import paths in it which are all a bit arcane and mysterious and

and sometimes we have to insert a middleware in there and it's like it'll make sure it's at the

very top or make sure it's at the very bottom or make sure it's in there but just after this one

or before that one and it's like what right right and why does the order matter it's definitely very

scary and and yeah it's automatically generated with the um start project command yeah um yeah

i mean i would say for years i was definitely in that boat of i see it there i don't want to touch

it i hold my breath when i have to put in you know white noise or something and and yet it is

not just some random setting it is you could argue kind of what django is in a core level

so um request.user so let's start with the request.user yeah you access the user right

the request the django http request object that doesn't have a user attribute so when you're

writing a unit test and you get you go factory request equals factory get and you create a

request object and then you go request dot use and it goes no attribute error you haven't got

that's because you didn't you you have to manually in the test you have to manually

set a user attribute but that's what django's auth middleware does for you it provides the

user stuff yeah so let's back up a sec let's let let's tee up what it is and then we can get into

this uh the the onion yeah so so just as a definition so the middleware is a callable

so that's anything that you can yeah like a function request and a response just like a view

and so that's something that basically just to like make this super simple in python that's

anything within parentheses so you can pass in parameters off in a function uh and that i mean

the technical definition which i don't think is that helpful actually is from the docs is a

framework of hooks into django's request response processing a light low-level plug-in system for

globally altering james that's that's a middleware now a middleware is a callable that takes a

request and it returns a response it's exactly the same as a view except a teeny bit different

right so it goes so if you think of uh what is the process of going to a web page because again

for for beginners or people haven't thought about this for a while so you go to a url that takes you

to middleware wrapped around a view which connects you with a model and a template and then you get

something back so that's request response and yeah middleware is wrapped around the view so you can

access things before it gets to the view or after it comes to the view which we'll get to and i

think also maybe talking about whiskey is helpful here right which in your talk right okay so nice

job on this do you want to explain why whiskey is what matters python's uh stand web service

gateway interface it's like the standard for um web servers to talk to application service so

apache's got mod whisky express um garnicorn everybody uses or you whisky are the the service

that you put behind something like nginx um and they they a whisky application is a callable that

takes a whisky environment which is like information about the request like you know the method and the

request path and the query string and all these things and right i think what it it takes i just

interrupt i mean it imports the settings.py file loads all the middleware resolves the url to the

view and then returns the request object back.

That's kind of what a WSGI handler does, so it's another degree of abstraction on top

of it.

Yeah.

It takes this WSGI environment, the information about the request and this thing called start

response, which isn't really that interesting, but it takes this WSGI environment and it

processes that and it returns a response.

Any function, any callable, so that can be a class with a call or a function, that takes

a WSGI environment and returns a response, which is just an iterable containing the response

body, really, that's a WSGI application.

So every Django application has a WSGI handler, which is the WSGI application.

But Flask, that's also WSGI, and so are all the other Python frameworks out there.

Right.

And again, this is just, you know, why you need this is because the application, so Django,

And then the web server that is serving it are separate things.

And WSGI is a way that abstracts any Python-based environment so you don't have to deal with, again, whether you're using Unicorn or you WSGI.

Yeah, I think when we had Russell Keith-McGee on, he was talking about the pre-WSGI days, I think.

I think it was him anyway.

Yeah, well, and we've mentioned that.

Go ahead.

Well, the point is you had to deploy your web framework with your particular server.

Whereas now you can choose any server and mix it with any web framework, and it'll all work.

And that's been the case for a long time now.

And it comes at the cost, though, of unless you have a discrete reason to dive into all this,

you just see it there and you don't really need to understand it.

The cool thing about Django's history where it came before WSGI is that it plugs into WSGI.

So now with ASGI, which we've talked about the new async stuff,

Django is, it's easier for Django to switch to async things

than, for example, Flask and its current iterations

is WSGI, and there's some projects to work on that,

but it's going to be a little bit easier for Django, I think, than Flask.

Yeah, it's kind of amazing.

Like when you look in your Django project, there's this whiskey.py file.

And in there, there's just this callable that's getting…

Never look at it. Yeah, never look at it.

Yeah, don't go in there. There's nothing in it.

Well, you know, you might sometimes…

You know, you might… When have I edited that?

I've edited that when…

For Heroku, you have to go in there and change a line and hold your breath.

But, you know, when I was…

If, say, I was working with a third-party dependency,

so I've done this in the past,

working with a third-party dependency that was broken,

like the release version was broken.

So what I've had to do is import a fork of that

into my, you know, next to my project, say, on the server.

And then I've just adjusted the Python path

in the WSGI file to make sure that it was all set up right

to import the overridden version of my dependency

rather than the broken one from PyPI.

Right, and Python paths, that's also scary.

Yeah, exactly, but unless you're doing something a bit,

it don't go in the whiskey but in there there's just this little get whiskey application function

and all that is is a wrapper which instantiates the this thing called whiskey handler um and that

it's just two lines and it's like well why is it there it's there because just in case we need to

swap something out and so 10 years later or whatever along comes ASCII which is the asynchronous

um version of whiskey and it's very easy because there's this kind of um indirection layer to for

andrew to switch that in and so you know that's that's proven prescient after all this time yeah

it's nice yeah it's it's sort of like technical debt that helps rather than hurts for a change

well it's it's not even debt it's just two lines why don't we just inline it like you know it's

nice to see a decision that turns out to be quite helpful rather than harmful which is often the

focus is on things that are harmful rather than helpful yeah and then so anyway yeah inside your

application right your whiskey application gets a whiskey environment it's like a it's like a

request but it's not it's a python dictionary with all these weird keys in it you don't know

anything about and what django does the first thing django does is convert that into an http

request a django http request that we know and love that's got you know the access to the get

access to the post the meta the meta is the original um whiskey dictionary um so if you get

access request dot meta that's the that's basically what your whiskey application gets

django inserts a couple more keys in there but it's essentially what the whiskey server gave

you in its raw form and but what else does the we get headers we get you know we're able to yeah i

mean the important thing is we want to get to middleware but that's what whiskey is and it's

related yeah and then that once we've got that request we pass it to your to your view right

your view to your standard django view takes a request in and returns a response and all

middleware do is they do exactly the same thing but they sit outside your view so they wrap it

and you can have them wrapped one inside the other inside the other and that's in your settings file

that list from top to bottom is sort of from outside to in as they're wrapping each other

well yeah and there's i think and that's what they call the middle five different

yeah and there's five different types of middleware there's request middleware so that's

processing a request before it comes to the url uh in view section there's a view middleware

where you know the view and the URL parameters.

There's exception middleware

where what do you do if there's an exception?

There's a template response middleware.

I know I'm going down the list here,

response middleware.

Anyways, there's a couple,

you know, five built-in types.

The more interesting thing is what Django gives us,

which if you peek at the start,

you know, if you peek at the middleware setting,

you see, you know, there's a list,

you know, security middleware,

session middleware.

And the interesting thing is

what those are and why the order matters.

Why does the order matter, Carlton?

Well, why does the order matter, right?

So they're called.

So the way to imagine it is that the request comes in

and it dives all the way through all of these middleware in order,

and then it finally gets to your view.

And then as the response gets returned back, it goes all the way back out.

Now, any of these coming down can interrupt and return a response early,

and it never gets to the one below.

So, for instance, CSRF, that cross-site request forgery, right, where you didn't have the little token in your post data, so it says CSRF invalid.

Well, that is actually executed just before your view.

So it uses a hook called process view, which, so it's kind of like, I'm going to let all the other middleware do their thing.

And then just before the view gets executed, I'm going to check that the CSRF was in place.

and if it wasn't i'm going to return a response an error response there and then and your view

never even gets to see the request because it's right exited early um but like for instance

authentication request.user we were talking about just before right that requires the session

because if the session is not in place then it would break so session middleware has to be before

authentication middleware otherwise it breaks so this is where the ordering comes in and in the

docs there's a whole section in the middleware reference about which middlewares need to go

before what or where or after right and this i mean the authentication middleware this adds the

built-in user object so that you know that uh who the user is if someone's logged in if they're not

so it makes sense if you think about it enough that you need to throw that on before it hits

the actual view before you can't search on user if there's no user added in um so yeah so middleware

really is the secret sauce of Django this is where a lot of Django's power I mean there's the ORM

but this is kind of where a lot of the the magic happens well you get all like the security headers

that you get set the allowed host checking the I don't know and again because it's because before

it hits the view you're checking all these things to make sure is this is this what we want it to

to do does it have the user does it have sessions i mean maybe so common middleware is an interesting

one i you know the name sort of uh that's the one actually when i just look at it i go actually i

don't know which one that is because the other ones all kind of say what they are yeah because

like security is obvious sessions obvious csrf is of it what's in the common options yeah well

what is the honest answer is maybe we need to we probably need to go let's go look at the dogs

yeah well yeah so this is you know let's just so everyone's listening so the Django fellow and you

know I teach this and we're like well okay so what's in common middleware right uh without

looking oh to be honest I can't remember we can we can look we can edit to sound better

no let's leave it live like you know are you wanting to um so what are we doing we're going

to the Django docs where we go all the time um okay and it just says that it adds so it adds

some I like this it adds a few conveniences for perfectionists so right well no I'm looking in

the source code so I'm having a look what's going on you had to one-up me like that okay well you

know so um those are the two places okay right so in in common middleware we can forbid access

to disallowed user agents.

So if you don't like people who use Chrome,

you can disallow the Chrome user agent in.

You wouldn't do that.

Right, you can append slash and prepend.

Or you can form rewrites in the URL.

Oh yeah, append slashes.

So rewrite if the append slash isn't at the end.

What else?

So it is just a grab bag of other stuff we need,

but maybe isn't worthy of its own middleware on its own.

Yeah, right.

We'll call it common.

But also, this is where you can start to think,

well you know do i do i need this if you look for your base project it's fine but like you know if

you're really trying to squeeze every last ounce you can start tripping some of these off you may

not need common middleware you so you could get rid of it you might not need security middleware

right okay so is what's that going to check that you were serving um um https you might not want

that check because you might know that you're serving https you might have nginx configured

to force that the tricky thing is i wouldn't recommend this unless you really know what

you're doing but going through the middleware and starting to strip them out and then if you

need that high throughput api endpoint you can run that in a separate service separate service

which has a stream trimmed down middleware which is grease lightning you know well and this is

we've done some interviews with some large django sites at scale that we're going to release soon i

mean this brings up fighting the framework you know because i the danger is you can go and doing

this do these things but then you need to really be sure you need to do it because this is really

the internals of Django so maybe there's a tiny performance benefit but this isn't now this is

pretty core Django which I always get a little uneasy to touch because there's updates to Django

all the time and you know you don't really want to eject from it unless you really really have to

in which case maybe you would do as you said set up a separate server just for the one particular

thing yeah exactly add a degree of caution like it's fun to play with it but don't jump off the

rails of django unless you really really have to right but if you're not using sessions if you're

not using authentication if you're not using the message messages middleware yeah and you need a

speedy endpoint you can trim those those layers out and yeah at that point you know i mean that's

and that would be an interesting experiment to to do you know and probably you would find it's

faster um yeah but for general and yeah for general use when you have stuff right as soon

as you want the sessions or users or you know the messages frame what does the messages framework do

that that enables you to say a um say a user saves a form right or saves it saves an object

that enables you to set a message on the the rom request and they get redirected to a fresh page

right and then it comes up with a little message at the top saying hey you you successfully created

you know your new blog post like it does in the admin if you create a um a record you kind of want

that that's great so don't get rid of it yeah well django is eminently customizable uh but as i tell

people be sure you know what you're doing um and i do want to get to so this all leads in so your

talk um your talk was about using django as a micro framework and i will we'll link to it i

thought it was great you sort of showed that django gets a bad rap for being bloated and not being as

concise as flask or express or some of these things but in fact and you gave an example which

we'll link to i i tossed up a repo yesterday of a how many lines is that 12 line hello django

file where using especially the middleware and the whiskey handler you can actually not using

the middle using the whiskey handler you can um treat django just as lightweight as any of these

other things because of course you would never use flask just the seven line version that's just a

demo um yeah no like the hello world from any of these micro framework sites they all look great

and but django doesn't do that django has the whole start project and here's the views file

and here's the templates over here and it's all a bit like wow this is big and bulky it's not bulky

at all what and what the point of my talk was to walk everybody through down to the base handler

which is where we get we you know we fundamentally that's really the point yeah we take in the web

requests we process it in our view and we send back the the response with like the web problem

it's what all the frameworks are doing and that that core base handler um class it's like 100

and something odd lines, 150 lines long, 160 lines long.

It's not, and that includes all the exception handling

and all the ability to load the middleware chain

and all these things that it does that is the core framework.

But it's a 160 line, there's no room for it to be clunky.

There's no room for it to be slow.

There's no room for it to be, it's lightweight.

It's the same as any other web framework out there

because we're all doing the same thing.

We're all taking requests, we turn them to responses.

bit is as fast as anything you want yeah and i'm just looking up the um the actual location of that

is that the whiskey org or where is the because i want to link in the show notes to the to the

actual file so if you go into django core handlers base the base handler um in the base handler class

in there is, well, what?

It's 120 lines long.

Okay, yeah, base.py.

140 lines long.

It's 140 lines long, right?

Wait, Django core handler is base.py?

Django core handler is base.py, yeah.

And the class, base handler.

Oh, the class, right.

The overall file is 166.

Yeah, like what else have we got going on down there?

We've got, yeah, a couple of utility functions

outside the class.

So I'm going to put this in the show notes so folks can take a look and see it's not as scary as it may sound.

Yeah, I mean, the idea of my talk was just to break open the box a little bit

and point people to these core handlers and say, look, there's not actually that much going on.

Right, and once you see how the handler works, then you see why this Hello Django example is so concise

and that a lot of the, I was going to say cruft, a lot of the additional things Django gives you

is it does give you all this middleware and it pre-builds out a structure that makes more sense

for a larger site, not just one with a couple of routes, which is quite nice because otherwise

we'd all be arguing about the proper way to structure our Django apps, which is what happens

in Flask and Express. And instead we have the opposite issue of when you get really, really big

and people say, oh, Django doesn't do exactly what I want. And it's like, well, that's because once

you get to that size, everything is custom anyways. And where do you put the view, the logic?

You know, those are good problems to have.

No one can solve all those for you.

Yeah, right, exactly.

But that was actually at Django Boston.

I keep bringing this up.

There was a discussion before my talk,

before I gave a preview of my DjangoCon talk at Django Boston.

And the question was, what do you least like about Django?

And I think the top one was saying

it doesn't scale from zero to 100 all the way.

But, you know, nothing could possibly do that.

I think it gets you a lot of the way there.

And then, you know, at scale applications are different, you know, there's no way to

do that.

So I tried not to get too defensive about it, but, uh, you know, it's like, yeah, I'd

love if somebody smarter than me figured out all my problems I'm going to have in advance,

but, you know, maybe my application has lumpy traffic, you know, how does that traffic look?

That's going to change how I structure things, you know?

So, you know, I think it takes you pretty far up to the point where when you start complaining

about, you know, the view logic in Django, it's like, good.

Now you're, you've got good problems.

But this is my point about you can customize the middleware stack.

Yes, and you can.

If you want to, you can.

When you get, when you suddenly find, not suddenly find,

but when you sort of see coming up, you know,

over a course of months as your web application grows,

you see that you're coming up to performance problems.

You know, it's not like something that happens overnight.

It's something that you realize, okay, we can scale up another worker,

and okay, fine, but actually we're going to have to kind of address this.

And then you start looking into the structure,

And you're like, well, actually, we could just rip this bit out and we could serve these busy endpoints from a special, from, you know, a different application instance over here, which is optimized for those endpoints.

You can do all of these kind of things.

They're not beginner things, but they are all available.

And all you really got to do is dig down and understand how the framework is put together.

And the point of the talk was there's only 160, 200 lines to understand.

It's not that complex.

Right.

And I think the thing is, we've talked with folks about the popularity of Flask, which has many great attributes, but one of the pressures is that you inevitably want something custom.

And I think, if I might put words in your mouth, the point of your talk was you can do that with Django, and this is how you don't have to spin up something else in Express or Flask or whatever.

You can do it just with Django.

Yeah, I mean, I would, you know, there might be some really niche embedded thing where Flask isn't going to help me.

I'm going to need Rust or Go or, you know, I'm going to need something really hyper-performing.

But if I'm still using Python, there's no reason at all why I would ever stop using

Jack.

Right.

And again, I mean, you know, we were talking with David Hennemeyer Hansen.

I mean, I agree with his comment that it's never really the language's fault that you're

fighting.

It's always the algorithm.

And then, OK, maybe if you're Google, the language matters.

Or in these tiny cases, rewrite something in C or Rust.

You know, these 0.001% cases, one in a million companies, there's 10 companies that need to do

this series. You know, they talk about, oh, that's not web scale. What are you talking about? There

are three companies in the world that are web scale. Right, right, right. Okay, well, I think

that hits the points of what we want to talk about middleware. So, you know, don't just gloss over it

when you see it in the settings.py file. Take a look at Carlton's talk. Take a look at this repo

just to see how you can use Django as a micro-framework.

And it's just really interesting.

I mean, I think this is,

the more you start to understand Django,

the more you see that it's not that complicated.

And at the same time,

it gives you the power to monkey around

with the internals if you want to.

It also helps you appreciate that

maybe I just want to kind of let it be

unless I really, really, really have to.

But I could if I wanted to.

Yeah, like, you know,

there might be some apps

where you don't want the admin in.

You don't want this, you don't want that.

But, you know, there's some, not many.

It depends, right?

That's our tagline.

Yeah, it depends.

Yay!

Welcome to Django Chat.

Yeah, we got it in.

We got it in.

Okay, well, thank you everyone for listening.

As always, episodes are on DjangoChat.com.

We have an email newsletter if you prefer to get emails when episodes are released.

And we're at ChatDjango on Twitter.

We'll see you all next time.

All right, bye-bye.

Thank you.