Django Admin at Scale: From Milliseconds to Microseconds 🚀

This video features Sumit Singh at DjangoCon Europe 2025 in Dublin, Ireland.

Django Admin at Scale: From Milliseconds to Microseconds 🚀
0:26:53
Published June 13, 2025
778 views

Talk: Django Admin at Scale: From Milliseconds to Microseconds 🚀 by Sumit Singh

https://pretalx.evolutio.pt/djangocon-europe-2025/talk/WKMHAU/

Summary

Django admin provides rapid CRUD, validation, authentication, permissions, and customization, but it can become unusably slow as tables and related data grow. Sumit Singh describes a production incident where admin pages timed out while loading campaign metrics, then explains how query counts, pagination, admin actions, change forms, filters, and caching affected performance. He recommends diagnosing with Django Debug Toolbar, logging, Silk, `QuerySet.explain()`, and observability tools; using `select_related`, `prefetch_related`, annotations, keyset pagination, bulk updates, batching, raw ID fields, limited form querysets, deferred fields, and targeted caching. His main argument is that admin performance requires continuous measurement and maintenance: optimize query count as well as query speed, test with realistic data, monitor slow requests and index usage, and keep implementations as simple as possible.

Key takeaways

  • Use Django Debug Toolbar, Silk, query logs, `QuerySet.explain()`, and production monitoring to identify admin bottlenecks.
  • Prevent N+1 queries with `select_related` or `prefetch_related`, and reduce query payloads with annotations, `only()`, and `defer()`.
  • Use keyset pagination for very large tables because it avoids scanning and discarding rows skipped by offset pagination.
  • Make admin actions efficient with queryset updates for simple changes and bounded batches for complex per-object processing.
  • Avoid loading huge foreign-key and many-to-many choice lists by using raw ID fields, filtered querysets, and collapsed form sections.
  • Cache stable or read-heavy data selectively, and maintain performance with realistic tests, slow-request alerts, profiling, and index monitoring.

Summarised automatically from the transcript.

Chapters

  1. 0:00 Django Admin at Scale An introduction to Django admin, its rapid-development benefits, and the production incident that exposed its performance limits.
  2. 4:42 Performance Bottleneck Diagnosis A roadmap for diagnosing, solving, and maintaining Django admin performance as data grows.
  3. 5:27 Common Admin Bottlenecks An overview of N+1 queries, inefficient filters, memory-heavy actions, and slow change forms.
  4. 8:37 Profiling and Monitoring Tools Techniques for investigating admin performance with Django Debug Toolbar, logging, Silk, query plans, and observability platforms.
  5. 9:26 Query Optimization How select_related, custom display methods, and annotations eliminate unnecessary database work.
  6. 10:59 Keyset Pagination Replacing offset-based pagination with indexed keyset pagination for large datasets.
  7. 12:29 Efficient Admin Actions Using bulk updates and batch processing to reduce query counts and memory usage.
  8. 14:01 Optimized Change Forms Reducing form load times with raw ID fields, filtered many-to-many widgets, limited querysets, and deferred sections.
  9. 14:46 Targeted Caching Caching querysets and list-filter options with Django CacheOps, plus cautious use of raw SQL.
  10. 17:56 Performance Maintenance Monitoring slow admin requests, profiling regularly, tracking indexes, and testing with realistic data volumes.
  11. 20:17 Production Case Study How static filter choices, deferred fields, and cached counts resolved a real notification-log admin timeout.
  12. 24:29 Questions Discussion of performance tests, DRF querysets, and alternative caching strategies.

Transcript

3,959 words · auto-generated Show

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

0:07

Speaker 1: Uh thank you for that introduction. Uh well uh congratulations everyone. You're finally uh made it to the last talk of Jangukon So yeah. I know I know you all need to prepare for uh after party, but yeah, we'll quickly uh wrap this up. So yeah, uh Jen Weadmint scale Let's start. Uh first let's talk about Django admin, right? Because it's basically a dream, right? Because without Much uh without doing anything you get to so much, right? Like based in cred operations without writing any code, just you know initializer models And uh form validation and generation, that too, without anything. And authentication and permissions out of the box. That's really cool, right?

0:53

Speaker 1: And also you can easily customize it with a couple of lines of code. Yeah, basically a dream Uh the I believe the best thing uh since sliced bread, right, for rapid development. Now Let's talk about a real story. Like to be honest, is the big reason I'm here. So last year I was consulting a streaming company where we had a critical table called notification log. This table uh tracked email templates, like uh when emails were opened, clicked campaign performance data and user engagement metrics. It was very critical for the business because our product and

1:38

Speaker 1: marketing team relied on the data. Because this data helped to identify what our user likes or dislikes so that uh upcoming campaigns can be designed uh basis on that actually. But one day a disaster struck. So have you been you know trying to open a page and waiting, waiting, waiting, waiting like Just to, you know, some data, some metrics. But after so long, uh you get this right. So yeah, so that's what our product manager saw. So, our product manager opens admin to check the performance of uh latest campaigns that were recently deployed. But after endless loading, uh what she sees?

2:26

Speaker 1: This is the good old I think sorry, sorry, sorry, sorry. Yeah, the good old 504 error. So uh the reality check here is when our database grows, so the Django admin can become very slow. But uh and on Slack all I could hear is that we need these pages working again, right? And they need to be fast. So uh the business impact like because of uh these five fours or the managers were not able to see the metrics They were not able to verify campaign performance. Marketing decisions were delayed. New

3:11

Speaker 1: features were put on hold while we fixed the admin, made it fast. And also the team credibility was uh damaged because uh we had uh legacy systems already which was working without issues and But with uh modern frameworks and this latest, you know, framework, you're seeing fireworks, right? Uh that should not be the case, right? I mean Django is fast, right? I mean we can definitely scale, right? So the turnout. So we implemented couple of uh we implemented a turnover to and fro, complete couple of techniques or like what to do and what's not to do in our code base. Which I'll show you today.

3:56

Speaker 1: And so the before what was happening is timeout errors greater than 10 seconds, basically fire four, first rate in teams, teams are not able to see your data and it also impacted business. But after Skoval techniques or you know, after implementing these things So the response dropped down to less than 200 milliseconds. But uh I know I know the title says microseconds, but yeah, you all were click-baited, so sorry. But yeah, so uh after our implementations, products managers were happy and able to uh quickly see the data and uh act on that. Then uh they were able to uh take data driven um uh marketing decisions actually.

4:42

Speaker 1: So yeah. Now let's talk about uh today's journey. So like but we'll be going through. So so first will be diagnosis like how to identify common performance bottlenecks like Uh what can be you know minor uh issues about Lex that you can easily identify. Uh then we'll talk about solutions like battle uh what we have uh implemented and what we have changed so that techniques I'll be showing you and then maintenance like how we can keep uh these performance over time like your code base grows and your database grows so that Now let's talk about like diagnosis, like what makes our Django in slow. I believe you

5:27

Speaker 1: all have probably faced this by now, I guess. The simple N plus one query problem, right? For example, this order admin, we are just showing the ID, order ID, total and status, and the customer name here, right? But behind the scenes, what it will do is it will make one query to fetch orders, but n number of queries to a fetch-related customer for each order, right? So basically n plus one queries. And when you go use Django debug toolbar, you'll see something like that, right? On uh on top you'll see some meta queries And the count query, which is from default page needed. But in the drop-down, we'll see uh lots of uh this customer queries, which is happening because of our customer name, right?

6:19

Speaker 1: So next one will be inefficient filtering, right? So we we have we can have lots of filters right in our uh uh models and table For example, in our product admin we have something like this category tags and stop, right? But your when your database grows, right It can contain millions of records, right? So for this filters will basically make full table scans and if you have multiple filters that you know You see where I'm going. So we'll talk more about this at the end because this is the uh main reason I'm presenting here because the filters were the main reason And uh another one is uh memory intensive admin actions, right? It's like we for example we need to uh

7:04

Speaker 1: bark uh product active it uh featured or not. So we can something like that we just loop over the query set and uh product is just set it true and save it. But uh let's say we have selected lots of uh objects at a single time. It can lead to out of memory errors with thousands of so and individual saves so and queries so that is uh one uh common uh bottleneck The next will be change forms, right? Uh In for example, in product, we can have you know different uh foreign keys which can contain thousands of you know options for like categories, tags, related to products, right?

7:51

Speaker 1: And many to many keys as well. So Django will load all options for each field, like all categories could be thousands, right? and uh all tags could be tens of thousands and uh other for related portals so yeah that can lead to very slow page loading and it will basically uh lead to five core. Next. So these were the common uh border legs you will probably see Now how to diagnose your own admin, right? Like these were the some issues which I faced. There were some others, but yeah, those were the very common So how will you diagnose? So first thing is officially Django debug toolbar.

8:37

Speaker 1: Please use that. If you are not using, please do use that Really helps a lot. You have seen uh the screenshot before that how the queries will be visible from that uh uh screenshot then uh you can uh write your loggers loggers that basically logs your database query logs like how much time is taking how what is happening behind the things you can use those And uh Django Silk Profiler is also a very nice tool if you're not using please do check it out. Then you can utilize queryset. explain like uh index is being used or not, what's going uh in the query. So you can utilize that as well Other than that, the uh different tools like New Relic, DillDog, and Century Apim is very nice tool. You can probably utilize that to monitor

9:26

Speaker 1: How let's come to uh solutions like how uh we started to improve our uh jangment to make it a bit snappy rather than throwing fire fours So let's talk about the N plus one problem, right? It can be easily solved by strategic uh related field loading. For example, we are here showing the ID, customer details, total and status. So before uh using that list select it, we were causing n plus one queries. Now if we just use that and it'll just One query it basically creates a join in SQL and it will fetch the customer with the orders. You can also write custom

10:12

Speaker 1: display metal just like we are doing here. If you have any custom thing you want to appear on that admin page, you can utilize this or something like that Also you can use uh preloaded annotations which basically uh helpful for calculated fields like do you don't want to do uh every time to uh for every object you can just On query set level you can just annotate this and it will just utilize that. Next is uh pagination. Before I talk about this, in Jenguen 2022, Tarun gave a very nice talk which covered different pagination techniques This can be a very good starting point for implementing customer pagination. So if you have not checked it out, please do.

10:59

Speaker 1: So here uh let's just say we have lots of records in our uh a table, for example. By default a list per page is 100. So if let's just say we have millions for record like 100 is very small, right? We can probably increase it So a better approach is by default it uses offset limit pagination to make it uh Optimal. We could use key set pagination or some might of you know as this cursor pagination, I believe. So this is a custom implement implementation which I'll be showing you, like how we can utilize. So it basically uses the rather than using uh limit offset.

11:44

Speaker 1: We use the primary key to fetch the ID from the previous page and use that ID to Filter the records for the current page, right? So it will basically perform better to our traditional limit offset page initiation on a larger dataset With uh custom pagination like this, we can easily paginate through millions of millions of records. Here we as I told, like using the last ID from a previous page and then filters the query set Major benefits in this approach are that filter can use the index, right? Index directly because usually we'll be filtering out the primary key, right? So already indexed. So it just Help whereas the offset limit offset uh pagination

12:29

Speaker 1: would need to scan and discard the earlier rows first and then we'll fetch others So let's uh this brings us to optimize uh admin action, right? Uh previously we were looping around just to you know Save our products just to market feature. But we utilize we can utilize the bulk operations without uh loading any of that. Like just use the query set update. It will basically update all the records in a single query. Super efficient. For simple operations rather than using loop, you know, very efficient. But now you all can say like what about the complex operations, right? We need to do something before saving the object, right? And now that takes us to

13:15

Speaker 1: batch processing. So what we are doing here we are basically creating batches so that Uh it doesn't take lots of memory at a single time so that it basically leads to OM. So just create a batch of you know, let's just say for example thousand objects or five hundred. It's basically on your like how much you can how your server can handle create a batch like this and this process it batch wise it will be very efficient rather than uh by going oh looping over so it will basically help you Now uh change forms. So

14:01

Speaker 1: for foreign keys when you have multiple uh options or categories you do when you uh when you load that page it will basically start to uh time out cause let's just say a forum key has Thousands of records, right? So it will basically take some time. So you can replace it with uh raw ID fields. It basically just shows you the ID of that object and you can just go there And it will take you so it will save some save you some time. For many too many, we have filter horizontal. It basically helps you to load many too many keys, uh records easily without you know uh much taking much time. Now you can uh force your uh query set size as well using the overriding get form.

14:46

Speaker 1: You here we are I'm uh for leaded projects I'm using I'm Putting in a limit for how much products I need to load. So that you can also do. For expensive field like which needs some processing. So you can utilize this field sys. Which collapses classes to uh defer loading expense expensive form section. So it's basically helps you save some time Now uh interesting stuff, targeted caching. So anyone here have used uh Django Cash Ops? If not, uh that's great. If not, please do check it out. I highly recommend it

15:32

Speaker 1: Very nice library, handles most of the things automatically like cache invalidation, query state invalidation, everything. So and it is very stable and uh production ready. So please do check it out So here's an example of uh custom uh model admin which we can utilize. Here we are just caching our query set for uh the timestamp we can provide like how it timestamp will be uh the timeout will depend on how frequently our data is being changed You can uh uh put your as per your need, you can put anything like that. But the cache S is provided by cache ops. Basically it will uh create uh quiz caching in uh your whatever uh cache you are using, memcache, redis, or it works with.

16:17

Speaker 1: all of those so it basically uh caches your query set and it will it will be utilizing every every time you uh fetch it Now, how we can use this? So just uh inhale it from our uh cache source metal admin and just uh sh uh set your uh timeout for breads and it will basically uh start using our cache for uh fetching records. Next list filter optimization type. So standard it basically loads for uh every request like it will basically query uh your table to fetch the uh categories. But uh what we can use is uh override this admin simple list filter and uh just override the lookups method

17:06

Speaker 1: And by caching, you can just limit our options by using a query set and it will basically use the caching option. So it will improve the your list filter optimizations. And next is yeah uh when uh everything is not working for you, then probably You can utilize raw SQL, but I uh don't highly recommend it. But use sparingly with Constitution because soon your code will contain more SQL and less Python. So yeah, I have seen it. So yeah So that were some solutions that uh we have implemented and improved our uh performance of our admin pages Now, uh maintenance, like how we can uh keep these performance gains over time, right?

17:56

Speaker 1: So monitoring strategy, it basically depends on your use case, like how you're growing and how your code base is growing, but these are some. uh techniques which I have seen or have implemented. Basically you can create your uh custom middleware to track uh admin performance uh setup alerts as well like for slow as let's just say it's taking more than uh 200 milliseconds so you can just log it somewhere and you can just you know work on that like what it's uh how why it is taking so much time Then you can also have uh regular profiling sessions. So uh we used to have bi-weekly uh Sessions for our API uh like what uh last two weeks why there's a spike so then we started to uh talk also about uh like why this Django pages uh spike

18:45

Speaker 1: why what was the reason like why this went slow so we had to we uh started talking about that as well in our sessions Then uh last thing is you can also use uh monitor your index uses because it can also uh become slow when you when it grows, right? So a sample performance middleware can be something like this. You basically in your middleware you can just basically log your like It uh went below the threshold. So you can just log it and you know see what uh is going on. You can just improve it later on. Like also you can do performance testing as well, you know you add add uh admin specific performance tests

19:32

Speaker 1: Try to measure with uh realistic data volumes. I know it's a bit difficult to uh replicate your production uh load on local, but it's manageable somehow Also uh you can simulate uh real user interactions, not just page loads So a simple uh performance test can look like this. Like we created uh 10,000 test records just to test like how much time it will take. So it basically depends on your needs, so you can just utilize that. So uh real world example like what we uh what I or my team did So this is uh what my

20:17

Speaker 1: log admin looked like. So it has you as you can see it has six filters We uh for pagination we were already using no count paginator, so no uh count query. So yeah And uh I believe uh we are already using listed list selected, but still it was throwing five course. So after uh digging uh checking out like what is taking some time using uh debug toolbar so the major problem was the lookups qs here right It was uh I believe at that point of time it was maybe overly optimized or uh uh it might have been assumed that these the categories which we are uh

21:02

Speaker 1: Fetching might be dynamic at some point of time, but the uh the values were static and came from constants, right? And it uh they were defined by analytics team so so that they can easily query. So uh they these categories can be returned without utilizing DB, right? We can just return these constant without uh db users. So what we did something like this. We let's just say we had categories like this. So we just created this value pairs and just returned it in the lookups rather than making query for that, right? So

21:48

Speaker 1: It uh basically takes uh us back to the gold old principle. Kiss, keep it simple, stupid, right? You don't need to query if these are in our constant right. So uh sometimes we try to make things complex which may not be required but sometimes it's required so basically on your you know need So uh the next thing what we did was started utilizing uh the only and defer fields. So uh we did uh so uh Since it's not a big table, we needed lots of table data, but we knew that we don't need this data. So we utilize the deferred fields or defer

22:34

Speaker 1: so that it can basically Just uh remove these fields from the query set. So it basically it uh improved the performance a lot. Also, we are using here if you can see the dot cache here, it's coming from uh Django cache ops So we can just utilize like what ops like get count and we can you can define your uh method what uh you uh what oh I'm spoken the term but In ops, you can just say dot git dot get. count, you can define that and it will basically cache that. And obviously timeout will use that for validation and everything. So uh the key takeaways was uh so uh in my local I was not able to replicate like why this is happening.

23:19

Speaker 1: So always try to diagnose with real data and to So what I did was uh installed a Django debug toolbar on my QA environment. It uh uh the QA team was not very happy, but yeah I had to do it And also like next thing is think about uh query accounts, not just speed. Because major of the uh as in previous talks I've seen that major of the Slowness comes from database, so that that too. So when optimizing, try to use uh built-in tools first like Slack dated only, defer, etc. It really helps a lot because Django is supposed to be faster, right? Then use cache strategically because uh in uh read-heavy applications

24:05

Speaker 1: the data might not be changing that much Also the last thing is monitor and uh maintain performance. So yeah. I believe thanks for listening. That is it.

24:29

Speaker 2: Congratulations on making your product manager happy. But in your case, and also in many other cases, I think you only run into these performance problems after they already hit production. Do you have any tips for us on like preventing common mistakes during for instance code review?

24:46

Speaker 1: Uh just like a tool like try to write a you know uh test performance related tests so that you know you have an idea like what it how much load your uh code can uh take at this point of time so Probably adding performance ready tests will help you. Also that admin that middleware I showed it basically help you to know identify problems earlier. So that might be useful for you

25:14

Speaker 2: Yes, thanks.

25:16

Speaker 3: We have one question online. Will this also improve crude query set operations that are carried out in the front end or not in the admin? For example, what we are using the Earth?

25:29

Speaker 1: Uh can you please repeat?

25:31

Speaker 3: Using DRF. Uh

25:33

Speaker 1: using DRF.

25:35

Speaker 3: Okay, I'll repeat the question. Will this also improve crude query set operations that are carried out in the front end and not in the admin? For example, what if we are using DERV?

25:47

Speaker 1: Yes, I believe using Slack traited only defer will definitely improve your uh API performance. And yes, definitely.

25:55

Speaker 3: Thank you.

25:55

Speaker 1: Thank you.

25:57

Speaker 4: Yeah, thank you for the talk and the overview of cache ops. And I was wondering if there are any other uh caching strategies that you tried and experimented with um before arriving at this um that you could talk about?

26:10

Speaker 1: Uh previously we were using normal uh Radio session. So it had lots of problem like invalidation and everything. So that's why we that's when we got to know about this library that it handles automatically most of the things for us. It uh caches the query set directly. So I don't need to you know convert it to a list and string format so that uh it's just add some more time. So that's why we got to know that Try to use Django GashOps will basically help us sometime.

26:40

Speaker 4: Thanks.

26:42

Speaker 3: No more question Well then thank you again for your talk.

Questions this talk answers

How can I diagnose slow Django admin pages?

Use Django Debug Toolbar, database query logging, Django Silk, and QuerySet.explain() to inspect query counts, timings, and index usage; monitoring tools such as New Relic, Datadog, and Sentry can add production visibility.

Discussed at 8:37

How do I fix N+1 queries in Django admin?

Use select_related() for foreign-key relationships, and use prefetching for suitable related data. Custom display methods and queryset annotations can also avoid repeated work when rendering calculated admin fields.

Discussed at 9:26

What is a faster pagination strategy for millions of Django admin records?

Replace offset-limit pagination with keyset, or cursor, pagination. Filter subsequent pages by the last primary key, allowing the database to use its index instead of scanning and discarding earlier rows.

Discussed at 10:59

How can I make Django admin actions more memory- and query-efficient?

For simple updates, use QuerySet.update() to change all selected records in one query. For complex operations that require per-object processing, handle records in batches so the entire selection is not held in memory.

Discussed at 12:29

How do I speed up Django admin change forms with large foreign-key and many-to-many fields?

Use raw_id_fields for large foreign-key choices, filter_horizontal for many-to-many relationships, and limit querysets in get_form(). Expensive form sections can also be deferred with collapsible fieldsets.

Discussed at 14:01

How can I cache Django admin querysets and list-filter options?

Django Cache Ops can cache querysets directly across supported backends, with a timeout chosen according to how often the data changes. List filters can be optimized by overriding lookups() and caching or limiting the returned options.

Discussed at 15:32

How do I keep Django admin performance from degrading as the application grows?

Track admin request times with middleware and alerts, regularly profile slowdowns, monitor index usage, and add performance tests using realistic data volumes and simulated user interactions.

Discussed at 17:56

What caused the notification-log Django admin page to time out, and how was it fixed?

The main problem was a list-filter lookup that queried the database even though its values were static constants. Returning those constants directly, deferring unnecessary fields, and caching counts with Django Cache Ops brought the page back to acceptable performance.

Discussed at 20:17

How can I prevent Django admin performance problems during code review?

Add performance-related tests to establish how much load the code can handle, and use performance middleware to identify slow requests before they become production incidents.

Discussed at 24:46

Do Django admin queryset optimizations also improve Django REST Framework APIs?

Yes. Using optimizations such as select_related() and only() or defer() can also improve API performance when the API uses the same queryset patterns.

Discussed at 25:47

What caching approach worked better than a normal Redis session for Django admin data?

The speaker moved to Django Cache Ops because it handles cache and queryset invalidation automatically and caches querysets directly, avoiding extra conversion work.

Discussed at 26:10

Presenters

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

More videos from DjangoCon Europe