Creating an Inclusive Django Community with Kenya Phelps
Published July 15, 2026
This video features Ara Anjargolian at DjangoCon US 2014 in Portland, Oregon, USA.
By, Ara Anjargolian
Since the days of version 1.0, the Django community has added countless features that address performance pain points; everything from cached template loaders to prefetch_related(), the staticfiles app to django-debug-toolbar. But how do you use these tools to make your site fast? In this talk we take a meandering survey through the Django/Python performance landscape.
Help us caption & translate this video!
Ara Anjargolian argues that Django performance work should begin with front-end optimization, since browsers typically spend 80–90% of response time loading and processing assets. They recommend long-lived caching with hashed filenames, bundling/minifying/compressing assets, using CDNs, and moving suitable templates and data to static files. Back-end optimization is more application-specific: focus on important slow paths, profile before changing code, then address excessive or slow SQL with tools and ORM techniques such as `select_related`, `prefetch_related`, `values`, indexes, field limiting, bulk operations, denormalization, or alternative data stores. Python bottlenecks often come from inefficient algorithms or repeated work in loops, while caching is a final powerful option whose main difficulty is deciding when cached data must be invalidated.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
So the first version of this talk was given at uh BuzzFeed headquarters, so I have an alternate title slide. So a few disclaimers first. So, you know, I try to touch on a lot of things during this talk, but it's by no means comprehensive. literally you know a hundred different ways you can add performance to your website. So I tried to focus on some of the main ones and some of the main areas. And so if I'm missing something, you know, don't take hold it against me. So I guess we'll get started. So performance. I think first it's useful to separate it out into two different kinds of performance and two different ways to deal with performance on your site. front end and back end and and they're very very different in how you deal with them and and and
uh the implications thereof um and they require like significantly different approaches um so I guess First, um let's talk about front-end performance. Um when I hear people talk about performance a lot, they talk a lot about like, oh, I shaved 50 milliseconds off my you know endpoint or my my view or whatever. Um And really uh if you're doing website performance and you really should be starting with front end performance because um 80 to 90% of the user response time on a typical web application will be on the front end, not on the back end. So saving 50 milliseconds on your view is not gonna make a difference. Saving one second on optimizing your JavaScript loading procedures is gonna make a difference. So Steve Sauders , he's uh worked at Yahoo and Google at as
basically as a performance evangelist and he's written great books about um performance applications for O'Reilly that uh I would highly recommend. But um let's start with the front end Um so front-end work. So um the attributes of front-end performance work um they can be based generally universally applied. So what does that mean? It means that When you make a fix or improve something, it's gonna affect almost every view or every every page of your site. Um they often require systems and tooling changes. So it's not something It it's a lot of it is like dealing with, oh, I need to override collect static to do something. I need to compile something and put it in a certain place so that it works. So there's a lot of systems tooling. If you guys have op teams for bigger companies, a lot of times your ops people have to get involved with some of this stuff.
And uh another attribute is it they're often clear system independent best practices. So this is not often the case with uh backend performance work. A lot of front-end performance work, there is a best way to do it. There is the known best way to do it. So I guess I'm gonna start with going through a few best practices and then talking about how you would do it in front-end performance. So the first one is caching static assets forever. So why would you want to do this? You'd want to do this because you serve your site up, you know, you're doing 10, 15, 20 requests a second. Your JavaScript or CSS, your images aren't really changing. So you don't want the browser to constantly be
re-getting those resources every time someone refreshes a page or new user, you know, they go to a different page that has the same JavaScript. So the ideal way to do this is to cache this uh um asset CSS file, JavaScript file, image file forever and only change it when um the image itself or CSS or JavaScript changes. It's called basically it's called fa far futures caching is what a lot of people call this. So how do you do this? How you do this, well before it used to be fairly difficult. Now uh you know ever since several versions ago Django has the static static files uh as a standard part of Django, all you really have to do is um Set your static file storage to be cache static file storage or if you have your own storage use the cache files
mix in. Suddenly if you look at um You open up like Firebug or Web Developer Tools and you see what happened to your images or your CSS , you'll notice that they're renamed like cat. jpg became cat. 123. jpg. And that's something that basically Django does for you and it changes the name of the file every time the asset changes. Therefore things are cached as long as humanly possible. So second best practice, bundle, bundle, minify, compressed static assets. So first of all, what does this mean? So bundling means you have 11 JavaScript files on a page. you you taint you take that you reduce that to one or two. Minifying means you take all extra white space and long long variable names, anything you can out of that JavaScript file. Compressing simply means to basically gzipping it.
So this is like typically this is done in by one packet. So in Rails this comes like standard. You need a basically a static asset manager to do this. The reason you'd want to do this is um reducing the number of requests is a strict benefit for making your applications fast. If if you ever look at um how applications are loaded. Like if you f if you fire up um Firebug and you look at the network panel and you look at how they're loading, you'll notice there's this like stair step pattern to how um assets are loaded. It'll load like eight at a time, it'll stop, it'll wait for one of those eight to finish, and it'll get another one. And and that's because um browsers basically have a limit per domain of how many simultaneous requests they'll allow on that domain.
Um this is a little bit better in the m more modern browsers, but if you ever have to deal with IE8 or or God forbid IE7, um they have, you know four four uh assets four connections to a particular domain at one time. So you'll notice if you have fourteen JavaScript files, it'll be loading four, it'll stop, it won't do anything until one of those is finished Downloading, it'll get another one, it'll get another one, it'll get another one. So what you're trying to do is you're trying to reduce the number of requests as low as possible. Um and static asset managers do that. Um So uh to to use you to do this kind of management basically you gotta use a um uh external package, two really good ones. Um Django Pipeline, actively developed, uh the guy you know he uh It takes a lot of pull requests and is always adding stuff.
And from the Flask world, Web Assets, which also has a Django web assets extension to it, also great at this stuff. So bonus points. The other other thing that definitely pipeline, I'm not sure about web asset support, is in your CSS files if you're referencing images and those images are under 30 2K, it'll take those images, it'll turn them into base 64, it'll embed it right in the CSS file. So um I don't know how many of you guys uh like maybe like Seven, eight, nine years ago, the best practice for um reducing number of requests for like uh websites that might have like 50 little icons was was having your designer create sprites and then using those sprites and doing these like CSS offsets to get to the next image and the next image and next image.
So with data URIs, you don't really need to do that. You can have 50 images, each with a little icon. You can reference them into your in your CSS file. And um the the br uh the static asset manager will find that, replace it with a base64 equivalent, and you won't have to deal with uh dealing with managing sprites. Okay, another best practice. Um ster serving static files via CDN. So CDN um is gonna provide less less latency to your user. Um if any of you like uh bigger shops like you guys do performance monitoring or testing and A lot of the performance monitoring tools will show you like the results as if your site was loaded in Brazil and as if it was l loaded in Russia and as if you know Because they'll keep servers in those areas and they'll tell you what the latency is. So you won't really notice it maybe locally because you're in New York and your servers are in Virginia.
Um but in Brazil, they might be getting a completely uh different experience of your website. Um so that's why it pays to use a CDN. Um so using CDNs in Django is uh fairly simple. I mean there's a a a bunch of ways to do it. Um one good way we like um use a extension called Django Storages, which provides custom storage uh classes. Um we we happen to use the S3 one. We put we um store all our uh static assets in S3, we point our CDN to S3, and we're done. Um okay another uh best practice um So serving more stuff is static assets. So this is a kind of a general one, but I think it's it's one that um is really applicable to so r A lot of I I think I'm pretty sure a lot of you guys now
you're using Angular, using Backbone, you're using Amber. You're doing less of your templating in uh HTML on the server side, you're doing a lot more of your templating on the client side. So as as a lot of these applications turn into like single-page applications where the server side basically just returns JSON and the rest of the stuff happens on the client side, there's an opportunity to store more and more things of static assets. Um as you store hemostatic assets, they take load off your servers, you can use the CDN, you can cache them forever, there's all these advantages. So front-end templates, like your Angular templates, your Ember templates are perfect candidates for using a static for serving of static assets. What that requires is a little bit of tooling. I mean it depends on exactly what JavaScript framework you're using and how your stuff is set up,
but it can definitely be done. We do it with Angular and it works brilliantly. Other thing you can look at is uh are there things that, are there pieces of data that only change in between releases? Like us personally, we have all these like fairly complex menus. They're really just like big pieces of JSON. We were serving them as as you know as part of like a payload on the server side and it was like getting stuck on the page and the page would would load it. uh via JavaScript, what we realized is we could actually just serve those as static assets, have Angular grab those static assets, and it added it took off several hundred milliseconds from a lot of our um key endpoints. Okay. So um let's let's go to back end
performance work. Um well you'll you'll notice uh so with front-end performance work it it it it's a lot easier to go through because there are some global um uh things you can just do and you can say this is the right way to do it, you should do it this way. Back end performance is um a lot more nuanced. Um Often it can be done on a only on a case-to-case basis. So every site is going to be different, the views are going to be different, your approach to making things faster is going to be different. A lot of times they require code chains. It's not just systems and tooling and using a different static file storage. You're gonna have to get in there, understand what that slope view is doing, and deal with it. Like I said, it's very site-specific, situation-specific. So there's not a lot of global stuff.
But okay, there are some global stuff you can do. Just to -dos you can check off your list. You can use cache sessions. By default, or I think by default, Django has uh DB sessions turned on, so every every session makes a few hits to the DB. You can switch that to either a pure cache session or a cached DB session and it'll take a few uh a few hits off your database. You can use the cache template loader. So a couple, I don't know, four or five Django's ago. Um uh the templates basically every time you'd make a request the template would get recompiled. They fixed that um and their solution was to use the cache template loader um which is basically just a Django setting you use it you use it in production it works it's strictly faster Maybe it's slightly controversial, but I I don't know if it is.
So if you're starting a brand new project or you have a existing project that is making like heavyweight use of Django templates, you might consider switching to Ginja 2. It's generally considered to be faster. And you know, I I would only do it on a new project or someone where it's really performance critical, because it is a pain if you're actually switching on and you have a fairly big site. But you would you should see some performance benefits from it. switching to Jinja 2 or starting a project on Jinja 2 instead of using the the standard Django um uh templating engine Okay, so now um kind of the real stuff. So uh so how do you start um performance optimizing on the back end? I mean uh first it should start with a disclaimer. Um You don't want to do this on every view.
You don't want to do it everywhere. You don't want to like go and say, oh, I'm just going to make my site faster. There's a waste of time because you'll be optimizing stuff that like five users a day touch. And unlike unlike front-end performance where it adds some complexity, but it's like a hard limit to how much complexity it adds Back end performance, you can go crazy. You can take every view and obfuscate it to get 50 milliseconds out of it. So you really want to pick your spots and pick the key points that are performance sensitive that hundreds of users, thousands of users touch every single day and work on that because typically performance um optimization on the back end is going to add code complexity. One programmer is gonna do it and the next programmer is gonna come and not understand what why this is being done. It's not often obvious.
You're doing a lot of times you're doing non-obvious things that only make sense if you're looking at it from a performance perspective So you really want to pick your battles. But let's say you did find a problem view, a view that was slow, that was critical to your application. How do you start? You don't want to just start like randomly like, oh, this you know, SQL query looks slow, let me go optimize it. Oh, why are we using this data structure? Let me go fix it. If you do that, you're basically flying blind. So what you really want to do anytime you do back-end performance work, you start with a profile. A profile basically tells you how long is this view taking to return, what are the elements of it. You know, what are the pieces that you should be focusing on? Um there's if you just Google
uh Django Python profiler middleware, you'll see like 50 of them. Um There's various good ones. I I linked to one that we like. I'm sure there's some that are just as good if not better. So what does a profile look like? You're not really supposed to be able to read everything there, but that's what a profile looks like Wha how it typically works is is um you install your profile middleware and you give it a um a custom like uh get param, you say and profile equals one. And when the profile middleware sees that instead of returning your normal view, your normal JSON, it returns something that looks like that. So what you see is typically you'll see the functions that are called, how many times they are called, how much time each call takes how much time the total calls take. This profile profile middleware specifically
it also gives you modules and how much how much time is spent in each particular module. So this gives you a map of, okay, I have this view that's slow. What part of it slow? Is it my function? Is it Django? Is it SQL? Is it just Python I'm doing? Is it this library that I didn't know about that I'm using? Where do I look first? So looking at that, typically what you'll see is you'll see like like let's say that's a SQL problem. You'll see 90-95% of the time sp spent in Django slash DB slash like mySQL dot py. Um so that gives you a good indication. Okay It's a SQL problem. If it's not a SQL problem, you'll see a lot of like various Python functions that are taking up a lot of time. So let's so yeah, things to look for. Tons of time spent in SQL, it's one place you can really pretty
pretty easily improve. Um functions that we called called way too many times. You'll see Basically every time we've done a profile on a like a performance-sensitive thing that seems like it's running slower than it should, um, we've often been emb embarrassed, almost ashamed of like what we've seen, like, oh my god, why is this function getting called 10,000 times? You know, it it should be getting called fifty times. There must be something wrong. So that's the kind of stuff you look for, things you don't expect and it's very site-specific. You know, you really have to know your application well to know what looks odd to you and what look what looks normal But that's the kind of stuff you're looking for. Either SQL or like, oh, this function is being called way too many times, or it's taking way longer than I would have expected given what I know it's doing. So let's start with SQL. What is the problem with SQL? You look at your profile, 90% of it's in SQL. So what do you do? You start with, there's great tools out there to help you uh
uh kind of diagnose and solve those problems. Django debug toolbar, which I'm sure almost everyone here knows, is is is a great tool. You install it, you you turn on the the SQL specific plugin, every every front end view you see you'll get a sidebar You open the sidebar, you'll see every SQL query, you'll see how much time each SQL query takes. Um that's very helpful to begin with. If uh where that kind of falls down is if you have um uh back end only views basically things that return JSON. So j Django debug tool won't really attach themselves to that because it actually looks for like an HTML to attach to. So in that case there's another tool called Django Dev Server, which basically replaces the standard Django server. And you can turn on different modules. And the different modules, one of one of the modules basically as
the view is As the server is doing work, it'll output every SQL query it's doing, and it'll output how long each query it's taking. It'll also it'll also like mark when the same uh query is being done 50 times. It'll say like 50 duplicates So on the back end, that's a really good place to start. So typically SQL problems they they fall into one of two big categories. One, the SQL query you're doing is taking too long. Two, you're doing too many SQL queries. So, you know, each of those have um different uh ways to solve. Um so There's basically if you if you look at every Django release, it seems like every Django release they retreat they they um release another way to get around a a a SQL issue.
So I think since the days of 1. 0, Select Related has been around, and then each release there's been they've been adding different ways to get your SQL to do what you want it to do. So I'm gonna go through a few of them. This list is by no means uh comprehensive, but it'll it'll touch a lot of the major things that we've seen that can can help with SQL. Um so select related. Select related um it solves a really common problem in Django. You have a foreign key, you have um uh car and and you have passengers and passengers refer to the car they're in. And you're iterating through passengers and you try to like print out the car they were in. And each query of a passenger will also do a query to get the car. So you'll see like if you're printing a hundred care hundred passengers, you're making a hundred queries to get the car. So you something you might expect takes five, you know, here's this view, it should be five queries long, you know, it's doing 105
queries Actually, if you go to Django Admin, Django Admin is really bad at this. If you uh ever open up Django debug toolbar to a Django admin where you reference another model inside the list view, you'll see 100 queries. If you're ever wondering why are Django admin is slow, that's usually the reason. So the way to solve this, select related. Select related basically you you you basically tell Django why are you getting the passenger also do a join internally in SQL and get the car so we don't have to get it a hundred times each each time we're iterating. Um so values, values, list. So a lot of times Um the Python object, the model that Django returns to you um when you do a RM query is is just uh Kind of getting in the way of you just getting the data.
What you eventually really do is just turn it into a dict and return it as JSON anyway. Um so it turns out there's like a lot of overhead with with creating this Django object that is your car or is your passenger It does a lot of stuff to make that happen. You can basically, if you know that you're not doing anything with the object, you're not calling any methods, all you're really going to do is you're gonna just take that and put it into Python dict anyway and return it. Um values, values, lists are two ways to do that. Um they basically return a list or they return a dict with with the fields you wanted. And you you can basically uh bypass a lot of that Python overhead. Um dB index equals true, I mean it's like you know, a 40-year-old C you know, ever since relational databases have existed, there's been indice indices basically, and a lot of times um you're you're querying by a particular thing that doesn't
have an index on it and it ends up being a lot slower than you would expect. So db index is one way around that. So an another page of SQL tricks you can use. So prefetch related. Prefetch related solves a very particular problem. So Select related that I mentioned previously, it doesn't work, in particular it doesn't work with many-to-many uh relationships. Um it's it's because internally Select related is done with a with a SQL join and it doesn't really map well to making that happen in one SQL query um by using joins. So what prefetch related does, it does the basically the same thing. It um It allows you to like, you know, let's say in our case, a passenger could have been in multiple cars, so so there's a many to many relationship. It makes that um work without doing a hundred
queries and it instead of doing the kind of the matching and and avoiding a hundred queries um in SQL, what it does is it actually does it in Django, in in in Python really. Um it it it gets the two tables you want, it does the matching for you, and it it allows you to um avoid a lot of extra queries. Um Prefetch related, even if um you don't use it in many to many queries, that they it does have certain advantages. Like we've seen um if you're doing select related on four or five different tables, internally in SQL it's like a five-way join, which is like uh typically a really bad big no-no. Um so sometimes prefed related um is uh is faster because is Python basically does a better job than than SQL would Um only. So only is um so if you if you open up Django
debug toolbar and you do your typical ORM stuff, you'll see that um Every time Django gets a model or or or a you know series of models, it's actually getting every single field from SQL. Um it's not really a problem. until it is, right? So you you have you have five or six fields on a you know a hundred row table, you'll never really see it as a problem. You have ten thousand rows and each of those rows have eighty columns And you happen to only need four columns in this view, you're just returning the name and the URL, let's say, and there's 76 other columns, then it becomes a big problem. So only solves this problem. Basically what only does is it says instead of getting all these 80 columns out of this table, get the four that I know I need.
And I'll just work with those. Um it kind of happens with a little bit of magic. So if you happen to um access a field that um wasn't in your only Um it'll it'll actually query and get the field so it won't fail. Um and this could be a good thing in the sense that your stuff won't break. Um why I have this uses users caution, it can also be a really bad thing. So if you're using only and you're getting these five fields you need. Another program comes along and says, oh, I need this six field. And they they access it without without ever thinking about it. Um we've had situations where suddenly this this this View is you know 3x lower and oh wait, what happened? Oh, something that was like five queries has turned into 500, because now each of each and every one of those iterations is going in getting the object again because it's missing a field that wasn't in the only. So you you want to be sure when you use only
that you are very, very careful with it. You you know, use it with caution, comment it well, you know, just be be careful. It's useful, but it you know, it it can also be dangerous. Um Defer is kind of the in in some ways the opposite of only. Rather than saying, hey, these are the fields I want, you're saying these are the fields I don't want. Similar thing, it's a performance thing. Uh I d I you know one common good thing you can do is you know you have 80 fields and and you don't want to get too specific about about exactly the fields you want, but you have these 10 big text fields that you know you don't want. You know you're never going to need in the context of this view. Um use defer, you take those out, and you you save you save that time that it takes to get that uh data. So bulk bulk create, so there's like now bulk functions, bulk updates, and bulk creates in in
in um In Django. So a lot, you know, most of your performance problems maybe are are you know read-based, but um you know we're uh like a financial data company. We insert a lot of data, we get it in in big blocks, and bulk crate has been great for uh uh uh basically speeding up the the um inserts. Um so what if all you know you those are some tools I think there's some other ones too but if none of that works um you can always fall back on raw. Django basically gives you access into hey I need to do this raw query um and just get out of my way. So you know raw is kind of a last last resort thing you can do, but it it can be helpful sometimes. So uh denormalization, I mean that's a classic, has nothing to do with Python and Django. It's a classic
um way to deal with uh uh performance problems as they relate to SQL. You know, a typical case, you have a blog and the blog has comments and you want to print the number of comments. So that can be really slow if you have to do uh if you have to do the query every time. So instead, every time a new comment is written, you update this thing called numments. um which keep keeps the running count of the comments of the block. So it's it's a very typical old performance trick, but it's one that's still, I think, very widely useful. And I just want to throw it out there, but it it is, you know, it is something to consider. Um if you're doing SQL, do you really need to be doing SQL? Um Uh oftentimes yes. Uh sometimes no. Uh I'm just using the SQL as a basically a key value store. Um I'm just using the SQL to search on.
Um well you know it turns out a lot of these a lot of these Specific domains are these days handled a lot better by alternate uh databases like Redis for your sets and lists and sorting, you know. Um uh memcache for key value storing, you know, you know, whatever the case may be, but it's something to consider um when SQL is uh seems to be failing you. Um so the other side of um uh performance optimization on the back end. It's it's Python. So um you know the you know you look at your profile and SQL is five or ten percent of the total total runtime. So it's not a SQL issue really, but you're seeing this Python stuff happen. So, you know, what do you look for? How do you deal with it?
So, you know, classic computer science, you look for bad algorithms first, right? You look for some things that are you know, n squared that don't need to be. You know, people will, you know, we've seen our we have had programmers in my company. You do something, you don't even think about it when it's, you know, there's 10 objects in the in the database. You know, a year later there's a hundred thousand objects and this bad algorithm you you did suddenly shows itself as, oh wow, that's n squared, now it's a big problem. Um so bad bad algorithm is something you you look for. Um You know, uh doing extra work in loops, uh, you know, I've seen that a lot. Like people people checking things inside the loop that don't ever change because of anything that happens in the loop. Like that's something we I've seen a lot. Um It you know, things yeah the other one like you know you have this function, it's like you don't you don't ever perceive it as slow
It does what it does efficiently, it returns the result you're expecting, but it's just slow enough that if you run it 10,000 times inside a loop, suddenly it's a big problem. So really basically performance on the Python or non-SQL side, I summon as uh up as uh people doing bad stuff inside loops, like Almost nothing is a problem until you do it a thousand or ten thousand times. Then it's a big problem. So that's what you look for. You look for your your your slow function or and then you try to Find that place where, oh wow, like why are we doing this or why does this take you know a a hundredth of a second except we do it a thousand times. Um So fixing the stuff, um so I want to give a warning. Like sometimes you just you have to get weird.
Like sometimes this stuff like doesn't make sense. Like what you're doing, no sane person would do it until You have this performance problem and you need to solve it. Um and I wanted to give two examples of of um things that we've seen in our code. Um so This is one we saw. So we saw decimal point one. It was used as a constant in a function. And turns out decimal point one isn't like a normal constant, right? It's a it's a function It's an object creation, really. And you know, at least until Python 3 uh 3. 3, um You know, the the decimal is implemented purely in Python, so it's kind of slow. Um so we had a function that had decimal point one and it was using it as a rand rounding exponent. It's like
something pretty common if you if you guys are familiar with the the decimal um module. Um it wasn't a problem until we had this thing that was returning 10,000 rows of data. Um it was it was rounding each one, so it was being run 10,000 or 20,000 times, and suddenly this thing was taking a quarter of a second. um this constant, like this thing that was doing nothing, right? So the solution for us was to make a really dumb constant at the top of our file called decimal point one that was evaluated at decimal point one and use it. It worked, it immediately suddenly your your profile, like, oh, there goes a quarter of a second off your profile. Um so this exact problem, and I guess we're gonna we're gonna do a pull request pretty soon. So we do a lot of stuff with decimal, this exact problem Is in actually the core Django code. If you look at how a decimal thing gets saved, one of the things it does is
it uses constant decimal point one. for its rounding. This exact thing. And it was it's just recently showed up in our profiles when we tried to save like 10,000 rows using bulk create with decimal. So it's something you just never think about until you you know you do your profile and you're like, oh what what's this? So example number two, like um reverse. So reverse is great, like you know, don't repeat yourself, just you know, reference the URL by name, give it give it its arguments, it'll it'll return the the the real URL Reverse is kind of slow. It's fine if you do it once or twice, but we had a we had a situation where we were turning a Excel table with with like 5,000 rows, it had the symbol, and it had the URL you could get to the symbol. It's just an output we needed. So we were running reverse 50,000 times.
So r on on the exact mind you the exact same uh URL endpoint, not different ones. It's the exact same one. So it looked exactly like this. Um and w And doing it with real data, it was taking a lot longer. So instead what we did is we ran it once with a placeholder argument, and then we used string replace to replace that placeholder argument with the real data. Kind of crazy, you would never want to do it, except when you're trying to make something fast and you need to do it, right? Um so that's kind of two like weird points. And Uh I guess I show these weird points not necessarily to say that you're gonna have these same problems. Well, if you work with decimal, you will probably actually have this exact same problem, but really to show that it's it's It's very uh application specific and sometimes you're just gonna have to do strange things to get things to go fast.
Um it's not obvious and it's not something worth even doing until you know that something is a problem because um no sane person would do this kind of thing. of stuff right you wouldn't do it as just like standard from day one you write this kind of code because you think it's the right thing to do. We do this thing like in very, very choice places that we know we have to do it because otherwise it's like it sounds it's like ridiculous. So what if um you done you've done all that stuff, your your your Python is as fast as it can be, you've done all the weird stuff, um your SQL is as optimal as you can be. Um and you're still it's not fast enough. Um so what's the the classic s solution to uh performance problems when you've done everything else? Um So cache, right? So cache and then cache some more. Anything you can cache, cache it.
Um so that r you know caching is tricky. Well, there's a saying that says caching is easy, invalidation is hard. Um So um yeah you can catch anything, but obviously users are expecting a certain have a certain expectation of how up to date your data is gonna be. So for you the challenge is okay um let me isolate um This thing that I want to cache that I know is slow, let me think about how often this thing needs to be, you know, changes or how often it needs to be up to date. Is it something that someone won't care about? um even if it's you know if the exact number is six hours old or is it something that's so important that you know it needs to be you know within a minute um So once you've um isolated these things, then then you you cache them. So how do you cache them?
Um so to start with Django, Django provides some tools. Um there's a view cache which which is a Python decorator you can put on any view and it'll uh it'll um Read the arguments of the view and it'll cache as a as a key based on those arguments in that particular view. It can be helpful. Um there's a template fragment cache, so you can is it basically a uh just so like a template function you put it around any any um piece of uh you know templating and basically anything that happens inside of that is cached. So remember that in Django Um queries are lazy, so if the query a lot of times won't be um evaluated until the template is like looping over the thing that the query returns. So it's going to cache all that stuff. Um so that can be useful.
Um but we've we've personally found a lot of the like more useful levels of caching aren't don't come necessarily standard with Django. Um For us, some of the most useful things has been isolating a function, whether it be a function, a cla uh a method inside an instance method inside a class or a class method inside a class and saying, hey, I want to cache this. So we have interfaces into stocks and the stocks have data series they return. So a really convenient thing for us to do is have the stock, it has a class method or instance method. And it returns this data based on arguments, and it's a very convenient place for us to say, hey, cache this for an hour. So for that, you need a basically a function level cache, something that'll look at that wrap around any function and cache. So a really popular caching uh package is called Django Cache Utils, which does um function level caching and it does a lot of other stuff
Um uh we h we actually wrote one called Django cache helper, which only does function type caching and nothing else, because we wanted we didn't want to deal with like other things the package did. We just wanted this hundred lines of code that can cache a package. So if you look If you Google Django Cache Helper, there's very short documentation and you can just use it. And you basically put a decorator around any method, instance method, class method, whatever. And based on the name of that method, the where it is, like what module it's in, and the arguments, it'll create a key and it'll cache it for however however long you you tell it to. So and then lastly um there's uh query caching. So query caching is something I think, at least I noticed get popular in Django maybe three years so three or four years ago where there's all these packages released um
that were trying to to solve the current query caching problem and basically how they worked is they a they attached either automatically or either implicitly or explicitly, you would tell particular ORM calls to say to cache for a certain amount of time. So two of these were one was called Django Cache Machine and I think it originated out of Mozilla. Um Django Cash Ops, I think it was just a kind of a guy like a consultants um and they tried to solve kind of two different things. One was just cache inquiries. The second one they tried to solve was automatic invalidation. So knowing when this query should be out of date and then clearing the cache uh when they know it was going to be out of date. So I mean this is a kind of a a big topic, but uh I wanted to put it out there.
So these things do exist and they are used. Uh I think Mozilla still uses Django cache machine. machine although I could be wrong. But you know they're complex and there's a lot of caveats. So invalidation is hard and knowing when to automatically invalidate a query is hard. You can still use these tools to like explicitly invalidate, but implicitly there's it comes with certain caveats, but it can be uh kind of extremely useful. Um And I think, yeah, a few minutes early, but that's that's the end of my talk. Any questions?
Start with front-end performance: roughly 80–90% of response time in a typical web application is spent on the front end. Saving a second from JavaScript loading can matter much more than shaving 50 milliseconds from a view.
Discussed at 1:06Use Django’s cache-based static file storage, or add the cache-files mixin to a custom storage backend. Django fingerprints changed assets—such as renaming a file with a hash—so unchanged CSS, JavaScript, and images can be cached for a very long time.
Discussed at 3:28Bundle JavaScript and CSS into fewer files, minify them, and compress them, reducing both file size and the number of browser requests. Tools such as Django Pipeline and Web Assets can provide this asset management.
Discussed at 4:15Use a custom storage backend, such as the one provided by Django Storages, to put static assets in S3 and point the CDN at that storage. A CDN reduces latency for users who are far from the application servers.
Discussed at 8:07Use cache or cached-database sessions instead of making database accesses for every session operation, and enable the cached template loader in production. For performance-critical new projects, Jinja2 may also be faster than Django’s standard template engine, though migrating a large project can be difficult.
Discussed at 11:13Profile the view before changing code. A profiler shows total time, call counts, and time spent in modules, helping identify whether the bottleneck is SQL, Python code, Django itself, or a library.
Discussed at 13:33Django Debug Toolbar shows SQL queries and their timings for HTML views; Django Dev Server can log queries, timings, and duplicates for JSON or other back-end-only views. Most SQL problems are either individual queries taking too long or too many queries being executed.
Discussed at 16:38Use `select_related` for foreign-key relationships, `prefetch_related` for many-to-many or other relationships that do not fit a SQL join, and `values()` or `values_list()` when model-object overhead is unnecessary. Index frequently queried fields, and consider `only()` or `defer()` to avoid retrieving unneeded columns, while being careful not to trigger extra queries later.
Discussed at 18:11Look first for inefficient algorithms, unnecessary work inside loops, and functions called far more often than expected. Code that is fast once can become a major bottleneck when run thousands of times, so profiling should identify those repeated costs before applying specialized optimizations.
Discussed at 26:12After optimizing SQL and Python, cache isolated work that is slow and does not need to be perfectly current, while deciding how fresh the data must be. Django provides view and template-fragment caches, and function-level packages such as Django Cache Utils or Django Cache Helper can cache methods or functions; query-caching packages exist too, but automatic invalidation has important caveats.
Discussed at 32:06Note: 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.
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 14, 2026