Creating an Inclusive Django Community with Kenya Phelps
Published July 15, 2026
This video features Liam Andrew at DjangoCon US 2017 in Spokane, Washington, USA.
DjangoCon US 2017 - Saved you a click (or three): Supercharging the Django admin with actions and views by Liam Andrew
The goal of this talk is to “save you a click”: while this is a pejorative phrase in The Texas Tribune’s newsroom, we love saving our reporters and editors clicks in the Django admin when they’re filing a breaking story. We’ve combined several helpful Django features, libraries, patterns and customizations – ranging from the obvious to the advanced – to supercharge our admin, allowing for live previews, shortcuts, and dynamic updates. Taken together, these tools suggest a framework for developing new admin features quickly and maintainably.
This talk would be especially valuable to Django developers who know the basics of customizing the admin, but aren’t sure where to take it next, or want to add some bells and whistles. It also opens up to larger questions about customizing the admin that would benefit more advanced Django developers. I would definitely allow time for questions and discussion to hear from others who might have tricks up their sleeves.
This talk was presented at: https://2017.djangocon.us/talks/saved-you-a-click-or-three-supercharging-the-django-admin-with-actions-and-views/
LINKS:
Follow Liam Andrew 👇
On Twitter: https://twitter.com/mailbackwards
Official homepage: http://liamandrew.info
Github: https://github.com/mailbackwards/
Follow DjangCon US 👇
https://twitter.com/djangocon
Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/
Liam Andrew presents practical ways to make the Django admin faster and more useful for busy content editors, using the Texas Tribune’s publishing workflow as context. He covers custom admin sites, richer change lists, computed and HTML-formatted columns, read-only previews, bulk editing, dynamic actions, row and object actions, confirmation views, inline actions, and nested admin interfaces. He argues that these features share a common pattern built around admin URLs and reusable mixins, and suggests combining Django’s model admin with Django REST framework and AJAX rather than replacing the admin outright. The approach can provide substantial functionality with relatively little code, but requires care with query performance, frontend assets, routing, and separation of concerns.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
Thanks everyone for coming. I'm going to be talking about actions and views in the Django admin. Mostly this is a lot of this is uh we're talking about some easy ways to customize it to get people some quick information. The joke is to save you a click. This is saving our admin users a click. So especially we're gonna start talking with a lot of tips and tricks in libraries. It might be an overload at first. It's gonna kind of go from We're beginner level and then we're gonna kind of uh abstract it out and talk a little more abstract at the end. Uh but the point is that there's a lot of ways to do this, to do some of the things that that we need to do with the admin. There are a lot of libraries that kind of overlap and conflict that are all kind of on the same page. So the conclusion isn't necessarily, part of it is to give you guys some tools and ideas and tricks.
But part of it is also to uh to sort of suggest other ways to think about the admin more holistically and provide a framework for it. Um and uh also I thought this talk was 45 minutes, so that's another reason it's gonna be overloaded because I'm gonna be going a little faster Than I expected. A lot of this is uh I've learned a lot of this by doing work at the Texas Tribune. I've been there for about two years. The Texas Tribune uh is based in Austin, we're a nonprofit, nonpartisan, all digital uh news organization. We focus on Texas politics and policy. We have a combination of news, data, and events. We also have two of my colleagues, Annie and Tyre, also here who focus on on these things as well. We'd love to talk to you more. But the idea is basically we have a lot of uh users, our CMS users are very busy, strap for time. They need to publish breaking news very fast.
But then we also do a lot of investigative features and longer stories where we want well-structured data where we can uh make sure that it can gets presented well all the time and on every platform that we're on. So it's a tough balance and we uh we do it all with Django. It's all pretty much vanilla Django right now. We're moving uh looking at uh moving towards a CMS framework like Django CMS. But at the moment, yeah, it's all pretty much vanilla. So we have found a lot of little tips and hacks and things you can do in the in that vanilla Django admin to make it really fast and easy for users to publish stories. Uh a little bit about my background. I've been at the Tribune for two years. Uh before that uh I was in a a program called Comparative Media Studies at MIT working in the hyper Studios Digital Humanities Lab.
And before that, I was uh working at a startup, uh news curation startup called Wiser. Uh I was battling the Django admin there as well, but I feel like I've really stretched its limits in the last couple of years at the Tribune We found that, especially when I first started with Django, when I was working with it, I really quickly wanted to add uh A couple easy buttons here and there, qu quick actions, quick views, just stu uh little stuff that shouldn't be that hard. And every time I I had a sad time And it it ended up with a lot of coupled files, a lot of coupled code. It just never felt right to me. I struggled with it a lot. But at the Tribune we recently completed an admin redesign uh a little bit uh on the just you know a reskinning of our admin and we I learned a lot it gave us an opportunity to re-architect uh some of the ways that we're doing some of the admin customizations
So if you think of the admin, the Django admin is a series of windows that are increasingly getting into a kernel of information you need, you start with uh the whole dashboard site. You know, the whole site. Then you move into a a list view, and then you move into a detail view, and then you move into inlines inside of that. I'm gonna kind of use that uh that zooming in as a framework for uh some of the tricks I'm gonna talk about in this talk. Um Then at the end I'm gonna try to wrap it all up into a bow and talk about why why uh why th they all have the similar patterns that we can learn from from. So yeah, again, the in part of the intention is not intention, but it might overwhelm. And it will be a lot, but hopefully it'll make some sense at the end. I'm throwing a lot against the wall. I want to see what sticks We're gonna be using an example site. It's called Newshound.
It's a blog by dogs, for dogs, and about dogs. And it also has a database of breeds, dog breeds, which were scraped from the American Kennel. club the other day. So if you can follow along at GitHub, github. com slash mailbackwards slash newshound. There's are gonna be examples there of everything I'm about to show, so hopefully it won't be quite as overwhelming. And I apologize in advance. They're extremely contrived examples. I ran out of ideas for how to talk about dogs. The data model that we have in Newtown uh is basically we have we have uh blog posts. And these have uh the you a post might be about uh might be about individual dogs uh that you're talking about. Each of these dogs can be part of multiple breeds, and that's
a custom through model because a dog can be 25% schnauzer and 75% chihuahua. And then each breed is part of a group breed group. So for instance a toy the toy group or the herding dog group or the terrier group. Again this is really contrived. but uh gotta start somewhere. So uh really quickly I'm gonna start with the admin site and landing page, a quick thing that you can do here to make your your initial dashboard uh a little friendlier is uh to have a custom admin site um on your uh on your Django admin. Basically uh this This lets you has a couple advantages. First of all, you can customize your site header. So now you'll see over here if I can get my yeah, over here. See it says news hound up here at the top rather than just a gym Django administration.
And then it also allows you to do cust do custom login templates. So here I made a custom login and I just added a forgot your password option down here, which is something that we we have on our on our CMS. So it's really handy to have this custom admin site to make really easy quick changes. And um it I also personally like that it puts all of the admin site logic into one module so you don't have these auto discovers going on all over and uh across different apps. You have a centralized place where all of your admin site logic lives. Uh moving into the change list views, this is probably the bulk of it that we have a lot of uh a lot of tricks up our sleeve here. Here's a basic changeless view with just the the the blog post. You'll see there's one article that's been published and it has a great headline.
The uh doesn't give us very much information though. There's uh there aren't a lot of columns here, it's not not much you can do. Uh so the first thing you can do is add list display to your uh To your change list. Uh this lets you add any other columns you want. So here you get a second column. Pretty straightforward. Most people know this one. But if you take a list display a little a step further, you can do a few other things. So first of all uh you can add extra fields that aren't actually on your model and then define them in the admin and uh tell them say say what you want the content of the uh the column to be. So in this case uh we're just listing all the dogs that are uh that have been sort of tagged or associated with this blog post. If you do this, don't forget to select related
or prefetch your related model because otherwise uh it will get heavy and slow very fast. The uh the other thing I want to talk about with list display, one thing we use a lot is to combine it with format uh Django 's format HTML. This lets us put any arbitrary HTML into those columns So you don't you uh you can kind of in this case for instance what we're doing um is we're for each dog we're putting we're making a little link hyperlink to the dog's uh absolute URL. And the uh manifests over here, it's hard to see, kind of hard to see, but uh you can you just get this list, nicely formatted list of names, and then you have a link out to go see those uh names The other thing I'll point out here is this is just an HTML entity for
an arrow. I feel like that's a really easy way to do visual communication rather than overloading somebody with walls of text or a bunch of hyperlinks. Just put little symbols in here. It's just text. It's really easy to do do and um and it makes makes it visually cleaner. Um I'm cheating a little here. I'm gonna talk about the detail view really quickly and then go back to the changeless view. But another way that you can use format HTML is with read-only fields and read-only fields very similarly if you set uh for in this case it's a photo preview This then you put if you put it in your fields, it will show up in the detail view and you can again put any HTML you want in here. So in this case you're getting a preview of this dog. This dog breed, I can't read it. But uh it's a cute dog. It gets into your admin.
Who wouldn't like that? Back to the lists view, another handy trick is list editable. This one here you'll see it turns this headline field into an open text field that you can just edit uh from the list view so if you had a hundred of them here you could edit all a hundred at once and then go click the save button at the bottom and uh and you're you're all set We find that this one, since it adds another button and it adds a whole bunch of uh form fields, uh it's not very friendly to users. I think it stresses people out when they see it. But uh we actually we use it it's particularly handy for cleanup sprints with is what we found. So for instance uh at the Texas Tribune we have a lot of tag data uh and some of it's very messy. So one time uh there there was one Sprint where we decided we're gonna clean up our tags. And so for those, I made a lot of fields list
editable for the time that we were doing this sprint. Uh and that was really helpful for making, you know, just getting in there really quickly, making the big alterations, and then uh and then saving I'm moving on from uh displays to actions. Actions at their most basic level, admin actions, allow you to basically check off certain items in that change list in the checkboxes and then perform a function based on what you've checked off In this case, this code is publishing all of the checked-off items that you had. It might uh set set them all to be published. One thing we started to do at the Tribune is uh make dynamic actions based on some of the choices that are in our models.
So in this case we have a few different states for publication You might have it in draft mode and edit mode and published mode. And here , this code is basically just making an action for each of those states. And then it manifests in the end with this list at the bottom of actions. You can set it to draft, set it to publish, set it to edit, and it will just change based on the models. Zooming out even farther, this is really tiny code, I'm sorry, but um even farther, uh we've started working on admin uh admin mix-ins or action mix-ins. In this case, what it's doing is basically defining, what you can do is define an action fields attribute on your admin model. And at that point it will add those action choices for you.
So in this case, and that works for either a Boolean field or for choices. So here, for instance, we have a post admin, which is a setting to either draft, edit, or published. And then down here, it's hard to read, but you also have a dog admin. Dogs have an attribute attribute for whether they're good or not. And in this case you can set the attribute to be you can set all these checked items to be all of them could be good, all of them could be bad, uh and it's just done with this nice uh handy mix-in so you don't have to worry about uh replicability. Moving a little bit more into the libraries and packages that we might talk about. What if you want to add actions for instance for each item in your change list. You know you have your you have your list of items and you you just want yeah button next at the end of each of them to to do something with that individual item
This is a really handy package called Django Admin Row Actions. And it basically adds that column for you down here in the corner. You'll see it has it has This is a drop-down called Actions. You click on it. Or you can make uh make all the dogs good, I guess. one. So it's usually defining a particular action or a particular URL and then it will uh it will do the rest for you. Another package that we use a lot is called Django Object Actions. This was developed actually at the Texas Tribune by our old colleague Chris Chang. It does a whole lot for us. This is one thing it does.
Basically it lets you put easily put change buttons in the changelist without modifying your template. So you'll see down here you have this publish all edits button that just shows up here in the corner on this changelist view. And it's really just like defining any other function here. And the one key part is this changeless actions part. And that's all it takes to add a button right there. We actually found was was harder than it should have been to do uh sustainably. Uh one way that we use these buttons for our models is we we often uh sync our models with external APIs. So for instance For instance, uh we sync with MailChimp or Eventbrite, and that that's where we uh host store a lot of our data for our events. So we might put a sync button right there so that people can quickly just sync up our CMS and our uh our website with whatever's going on externally
And it's it's a pretty easy place to put it. Moving zooming further into the change views, uh again with Django object actions, they have not just change list actions, they also have change actions And these are even handier, I think. So in this case, this just lets you uh take that particular instance and use that uh rather than uh having it be list wide. Uh here we are making all the dogs good in this particular article. So this article might have mentioned a few dogs. They're all automatically good if you click this button. Uh and also uh yeah it it maintains a lot of the admin features like message user. You can you know you can do a lot of the same functionality that you could do with any any view, any admin feature.
And I want to reiterate it, redirects make it really easy to add hyperlinks Remember the equivalent of hyperlinks without adding changing your template models. So in this case, uh this is basically a link. It's just you're you're going to a certain changeless view, uh but you're doing it with actions and that uh just is a little bit more uh moves it further into the back end and lets you sort of control it more. I also really like this handy takes instance or query set decorator that comes from Django Object Actions, which uh lets you put it on both the change and the changeless view at once. So that's uh that's a handy feature. And a shout out to Chris if you're here. Because actions are just views under the hood, you can add confirm views, confirmation views, kind of like the Django delete view
for resources. This is kind of a combination of the two things of the two sort of the action and the reader or the link. So in this case, just stepping through what this uh publish edited uh action is doing. You start with uh so it the first thing you do is it it it looks for a post request, it doesn't see one, so it just renders a confirmation view. And here's the confr uh the confirmation view over here. It just makes sure that you uh that you want to delete these things. Uh the second time around, uh if you put all you have to do, and it's really tiny text, but all you have to do is put uh an action value into the post data and it will send it straight back to this view. So you can use the same view to both render
the conf confirmation view and also process the confirmation when it happens. And this way you can, you know You get the best of both worlds and it's all wrapped into one nice little function. Going zooming further into inlines, uh we have a couple other packages that we have found uh Really handy here. This is sort of uh the the first one here uh is just a generic inline. This has no edits. Uh this is just showing a post can have multiple dogs uh and you can assign them Django inline actions is uh basically so we we earlier we saw Django admin row actions, which lets you uh change specific uh items from the change list. This is sort of uh another layer of inception from
that where you're now be able to uh edit and uh edit and process things from the inline view uh since uh Django admin row actions couldn't do that So in this case, you are again just getting a link to all of you redirects to a different resource. It's pretty easy to do. Just for reference, you can also use Django inline actions in the change list. So this is kind of redundant with Django admin row actions, which I saw or showed earlier, but uh it's good to know Another uh admin feature that is looks really handy. We haven't had an opportunity to use it at the Tribune, but I've always been interested in it is Django nested admin.
This is developed by our friends at The Atlantic and it's basically in line inception. In this case, you have uh breed group, so that might be like uh the toy dogs. And then uh in each breed or each breed group has multiple breeds and then each breed has multiple dogs associated with that breed This Django nested admin allows you to put all three of those layers right in one view. So here you're able to edit everything about the breed group, everything about the breed, or anything about the dogs themselves. But one and also does sorting for you. That's a really handy feature. If you have orderable fields, you can set it as orderable and then just drag and drop them. One warning is I would beware of overquerying without pre-fetching, and I'm not entirely sure how that works under the hood for uh for Django
nested admin, but just to beware of uh of doing too much with uh it because your view could get really really slow. So I'm done with talking about packages for now. I want to look a little bit at what we we could do next, uh sort of thinking about all of these together, all these different packages. and ideas. One thing that I found is that basically all of them are using this get URLs function from the admin. Get URLs is basically from the Django docs, it just shows you can add any views, any additional views to that model admin on the fly. And uh it's a really handy idea, and it's pretty much what how all of these different tools and mixins are using uh are are working.
Um so we have started to fold more of our admin features into mixins using and get URLs. And specifically we are interested in how to do this with Ajax views. We want to put a little bit more dynamic information and live updating information into our admin. But we and we want to be able to do it so it where it integrates nicely with the Django admin. In this case, uh I believe this example, we are uh putting in a preview of all the dogs that are currently trending. I'm not sure what's what's they're trending, why they're trending. But you might, you know, if you're a writer writing about dogs, you might want to know which dogs are trending so you know uh who to write about. So in this case, uh it it basically this get URLs function is just uh sending anything that goes to the trending URL back to that trending function.
And that's where a lot of the work happens. Here I happen to be using the dog serializer from Django Rest Framework to actually create the dog response. You could do it in a million different ways. And then the uh the JavaScript is is looks it looks a little long, but it's really not very complicated. Uh it's just uh calling an API and updating a container uh to And that this is sort of what it ends up looking like. And so down here you'll see this is this is just the changeless view over here. But above it, you get uh this Ajax-driven little module that shows you three dogs that might be trending. If you click this button over here that says refresh suggestions, you get three different dogs or maybe the same dogs if they're still trendy.
So this has gotten me thinking, like we're using this uh Ajax view, uh it's basically this JSON view, and Python or and Django has this incredible framework for doing just that, Django Rest framework. And I've been thinking about the best ways that we could be combining all the powers of the Django mod the Django model admins with all the powers of Django REST framework. So here I had the trending, I sort of changed this to the trending dog view instead of putting uh putting that view on the admin. And then here I made a new API view that uh is basically getting uh it's it's taking what was about a ten line function and turning into three lines and then also getting all the bells and whistles that uh that REST framework gives you on top of that of that. So that that's a that's a pretty handy pattern that we're interested in.
One step even farther that uh I've been thinking about We uh is this one is basically moving the trending algorithm into a model manager that under under the dog uh dog model and then uh turning this into a view set a full-on view set. I think in with so with the right pagination, this functions exactly the same way as that ten line uh that ten line view did. Uh except it's all declarative. Now there's no function really and and that feels like a better separation of concerns to put the trending into the manager as well. So this is a I feel like this is a really uh promising start to something, but it has no way, for instance, to get into the detail view to receive the context that the model admin gets, at least the way that I set it up. Um oh and this is just to to sort of finish that off.
This is the this would be the get URLs for this this view set. All you'd need to do is just add that trending uh URL and call it the call that view. And then you're then you're rolling. So yeah, I think there's something here, and all these libraries are kind of pointing to it. That uh there's the combining model the Django model admin and REST framework is something we're increasingly doing and we're curious, I feel like it's a very powerful combination uh and want to do more with it The uh could you know is there a possibility for instance of getting full CRUD capability uh on Django models uh with Ajax views right in the admin with all the context and and and all the richness that the mod the Django admin provides.
I think some open questions with that that we aren't sure about, you know, how do you, you know, what's the best way to integrate with the routers, uh, the router framework? what uh what what are the best ways to integrate to to put the admin context into the API view sets and uh what's the best way to manage your front-end assets, especially your JavaScript, your uh your CSS for uh whatever you're doing doing with the Ajax. But more broadly, we want, we, you know, we want our, this feels this feels like the right step to go. We want our admin to be an API client just like our website is, just like our external services are. It feels better architecturally, so we're we're interested in how to work towards it, but we also don't necessarily want to build a custom whole Django admin, you know, separate admin from scratch, because we just don't have the team for that.
Um, I think some of this is starting to beg the question of like why would we stick with the admin at all? Um and I kind of pointed to that a little bit. I but and I think one of the short answers is we have an eight-year-old website and it's a lot of built-up code and it would be really hard to to do that But the longer answer is that we're there's a lot of promise in the Django admin. You know, you get these things like versioning for free using Django reversion. get things like locking with Django locking. And the standardization and the community around that just just opens us up to a lot of uh really interesting interesting possibilities especially with the directions that the Django REST framework has been going. So we're excited, we're sticking with the Django admin.
by putting it on something like Django CMS or Wagtail. But that's the we're just excited about the future possibilities on the Django admin and we think that there's more that could be done with it. So I wanted to shout out to all the repositories that I or the packages that we used. Here's links to them on GitHub. Um and I think I think I covered them all. And uh to reiterate this is News Hound. Um and Uh quickly I'm gonna show a demo just in case some of that was hard to see Here's the dashboard for Newshound.
You've got breeds, dogs, posts, and breed groups. If you go into the posts admin, you'll suddenly get those trending dogs. I'm going to refresh we've got uh Chubb. Chubb is a Oh, he's a very interesting combination of breeds here. Um these dogs are not real. Uh Skittle So we refresh it, let's let's refresh it again. Let's just keep looking at dogs for a while. And then uh but so down here I'll show you a couple other features that we have in here. Uh again, we have that view breeds button, so you can just go quickly see all the breeds. You can publish all your stories that are currently in edited mode. So right now that's an edit. So if I publish this, I'm going to get that confirmed view. And I click and hey it's published. And uh and now you see the published status moved to published. Here's this editable field here. So if I want to make it dogs, then
hit save and it'll update that. If I Click in here, you will again get the same preview. And here you'll see uh evidence of the inline action. So you can see all the breed groups that Kira this dog Kira is part of. Kira is a hound. I believe this is where we'll see the inline action. So this is gonna or the nested admin, this is gonna load really slowly because I did not pre-fetch anything. But this is basically where you can edit pretty much the whole admin from one page. So that's why it takes a while. So all of this, oh here we go. Uh so here you have the hound. Uh here's the first one. You've got the Afghan hound. We've got Harley. Harley is an Afghan hound is is 100%
Afghan hound. So you can start to change any of those any of these details straight here from the admin. And you know this it's all nested. So the next one down, for instance, is the American English coonhound. Keep going. Uh so this is a really handy feature. So all of this combined uh are based this is all about uh I I think probably about a hundred, maybe two hundred lines of code, and we really got a lot of features in and uh it feels like a nice separation of concerns. Um I'm gonna stop there because I want time for questions and and And feedback, uh, we're again not really all the way there. Uh we have a lot of open questions, especially around how to integrate REST framework. So we want to hear more.
Create a custom admin site to change the site header and use custom login templates—for example, to add a “forgot your password” link. It also centralizes the admin-site configuration in one module.
Discussed at 4:55Use `list_display` with methods defined on the admin to show fields that are not on the model, and use `format_html` to render formatted HTML such as links. Related objects should be fetched with `select_related` or `prefetch_related` to avoid making the page slow.
Discussed at 6:26Add the field to `list_editable`; it becomes an inline form field so multiple rows can be changed and saved from the changelist. This is especially useful for short cleanup or bulk-editing sprints.
Discussed at 8:44Define actions dynamically from Boolean fields or choice fields, or use an admin mixin with an `action_fields` attribute to generate those actions automatically. This can provide actions such as setting selected posts to draft, edit, or published.
Discussed at 10:16Because admin actions are views underneath, the action can render a confirmation template when there is no POST request, then process the operation when a confirmation value is posted back. The same view can handle both displaying and confirming the action.
Discussed at 15:00Use `django-inline-actions` to add links or actions for individual inline objects, including redirects to another resource. It can also be used in a regular changelist, although that overlaps with row-action packages.
Discussed at 16:31`django-nested-admin` lets you put multiple inline levels—such as breed groups, breeds, and dogs—on one page, with optional drag-and-drop ordering. The speaker warns that nested views need careful prefetching because they can become very slow.
Discussed at 17:18Override or use the admin’s `get_urls` to route a custom URL to a view, then have JavaScript call that endpoint and update a section of the admin page. The example adds a refreshable module showing currently trending dogs.
Discussed at 18:48The admin provides substantial existing code, community support, and features such as versioning and locking through packages like Django Reversion and Django Locking. For that reason, the Texas Tribune planned to keep extending the Django admin rather than build a separate system from scratch.
Discussed at 23:41Note: 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