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.