The fine print in Django release notes

This video features Sebastian Rode at DjangoCon Europe 2025 in Dublin, Ireland.

The fine print in Django release notes
0:24:09
Published June 4, 2025
356 views

Talk: The fine print in Django release notes by Sebastian Rode

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

Summary

Sebastian Rode shows how recent Django releases reduce repetitive code in common school-data application tasks. Django 5.2 lets `reverse()` accept query strings and fragments, adds simpler query-string handling in templates and test-client requests, provides login-required middleware, makes admin foreign-key lookups easier, allows custom unique-constraint messages, and supports custom bound fields; he also covers form rendering improvements introduced in Django 5.0 and the query-string template tag from Django 5.1. His main argument is that these small release-note features make views, tests, admin customisation, and forms shorter, safer, and easier to maintain, while he notes the need for `select_related()` when displaying related data in the admin.

Key takeaways

  • Django 5.2 allows `reverse()` to build URLs with query parameters and fragments without manual encoding.
  • The `querystring` template tag preserves existing parameters and makes pagination links straightforward, while the test client accepts query parameters directly.
  • Login-required middleware protects views by default, reducing the risk of accidentally exposing an unprotected view; login and other views can be explicitly exempted.
  • Admin list displays can use double-underscore lookups directly, and `list_select_related()` avoids one extra database query per displayed related object.
  • Unique constraints can define their own validation messages, avoiding custom form-cleaning work.
  • Custom bound fields and form-rendering templates provide reusable ways to change labels and field presentation without overriding admin templates.

Summarised automatically from the transcript.

Transcript

4,202 words · auto-generated Show

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

0:00

Speaker 1: My first uh couple of words about myself. Well, I'm Sebastian Modo. Originally I studied actually physics in Munich, then uh did uh my PhD on high-performance computer simulations where kind of my love of Python started uh while doing data analytics and at the same time found at Jangsters where we do like web development. We have six developers doing a lot of uh public sector projects, especially in the educational sector. And most of the examples I will talk about are kind of derived from that. So we really it really occurred, made the code better. When I say public or like educational, I mean like we manage like data for schools essentially like test results, teachers can enter into systems. Just keep that in mind.

0:47

Speaker 1: Well, but first of all, well, let's celebrate, right? Django 5. 2 came just out. So a couple of examples will be about that release. But also, well, kind of looking at the timeline, we are here. So right, right, and the last long-term release was 4. 2. So many of you will probably upgrade from 4. 2 to 5. 2 And well, there's a bunch of changes which you can apply to your code, which makes your code probably nicer, easier. And well, yeah, these kind of things I will look at. Essentially to put it into pictures, right? Often and really often Django is really great. Like with a few lines of code, you can do a lot of things. But sometimes you have to work against Django with a framework. It's not really clicking And well these are the kinds of changes I want to highlight

1:33

Speaker 1: where with a new release suddenly you have less lines of code, it gets easier to do certain things. So yeah, you become from sad developer to happy developer essentially Um yeah, and with that I would directly dive into it. So what's the example, as I said, right? Like everyone probably went to school or has children that go to school or both. So We have a very simple example, right? We have a school A, a subset of pupils go to this school, and an other subset of pupils and like go to school B, and a pupil cannot go to both school. So that's the situation we want to model and that will be the example guiding. Us well kind of through the talk. So how would we model that in Django? Um well probably everyone knows, but still I will quickly go through it, right?

2:20

Speaker 1: We would have a class. Saying okay, class school derived from model. Let's say school has a name, it's char field, write an address. Another char field, let's keep it simple. What let's say one, two, three, fake street or something, and at least well in Germany or probably everywhere. uh they are somewhat numbered so you can uniquely identify them. Well again, let's say it's a five character number, so it's 001, 002, etc. And well to make it like identifiable in the admin or like to expose it somehow, we have a string method saying, okay, it's usually identified by the name. And we have pupils. Um, well, they are identified by name. Uh Peter Miller, for example, right?

3:05

Speaker 1: A birth date, which is a date field here. And well now it's what kind of a little bit of interesting comes in, right? There is a foreign key to a school because they're exactly assigned to one school and they have to have this key. Well, so foreign key relation, where we say, okay, related name pupils, so we can do school. pupils to get all the pupils of a school. And we say well undelete. If the school gets deleted, all the pupils get deleted as well. And we say again, pupil is identified by a name and well its birth date, here locally formatted. Yeah, just to make it more exciting. Okay, so with this, uh sorry, and then uh yeah, no, with this, I'll dive into the first one, which is URL building. So let's suppose we have two views, right? URL patterns here. So one that lists all the schools called schools

3:52

Speaker 1: here, where we say, um, okay, we list all the schools like school A, B, C, etc. A user can click on such a school and then he gets directed to the pupil's view where we can pre-filter this. Essentially to make URL building a little bit more complicated. So we list again pupils view lists all the pupils, but we want to filter, for example, via query set parameters. Here, name filter Peter and present school equals to one, saying okay, filter, show me all the pupils that go to school one, and which and the name contains Peter. Make it even more complicated, we could also add like a fragment, right? Scroll to the red box on the page or the last um element where the user used to be.

4:38

Speaker 1: So this is Used to be relatively simple. I mean for URL parameters this is simple in Django, but with these query strings added, it was kind of hacky. You had to build it yourself. There's three parts where we usually build this. It's in views It's in templates via the URL template tag or in tests where we use like the test client client get pupils. We have to build this URL in here. We will go step by step and see how this became better. So there will always be a before slide and then the after slide and then you in the Django release how you can improve that. So we have a school lists, right? saying okay well like iterate over the schools which we fetch from the database and now we have to build said URL so we say

5:23

Speaker 1: if schools. pupes dot exists Yeah, I mean if there's no pupils in the school, there's no point to linking to it. We build a URL, well, saying reverse, hey Django, look up the URL to pupils. list. And now we have to build this query string ourselves, right? We add question mark, we have to URL encode the dictionary, say URL encode school double point school ID to get this query set. part and we have to add the last position, same thing. We have to URL encode it so it works. Then we add it to the dictionary and finally render it in the corresponding I mean add it to the dictionary and then the last line render it in the corresponding HTML template. This is well kind of Rone, a little bit complicated, and now with Django 5. 2, we can just put these

6:10

Speaker 1: Uh query set and fragment parameters directly to reverse. So it's one function call here in the middle. Rest stays the same Uh we don't have to care about I mean encoding anymore. We don't have to kind of care about that it's non. Django takes care of all of this. Less code, easier. I mean it's a nice little uh uh gem which you can if you upgrade to 5. 2 simplify your code. Next one. Of course we can also say we don't want to build the URL in the view, but we want to build it in the template. This would look something like this. I mean you iterate like let's say schools is like school. object. org at schools. objects. org, right? So it's directly the database objects. We would again look up if pupils exist

6:57

Speaker 1: and then say, okay, first we're the URL template take, we look up what's actually the pupils list view. Okay, fine, that's like how it has been forever. And then we say we have to build this query set again. Question marks, cool idea. We have to use the template. uh filter URL encode to make sure spaces etc get properly encoded and finally um can also add the hashtag make it a link okay fine yeah in Django 5. 1 Already introduced there's the query string template tag, which well takes away all those template uh query string uh generations, so the question mark, several parameters, it gets all automatically um nicely added, formatted, yeah, less code, easier to use.

7:46

Speaker 1: Yeah, so really nice feature. There's one more thing where the query string template tag really shines is like if you have several Parameters. As you know, the first one has to have this question mark, school equals to A, second one is then and school equals to B, and page equals two. For example, so we have pagination added here, right? And like if you have pagination, you want to change this URL, you wouldn't need to write code like this, which essentially Um takes the query set, the keeps all the filters you already have in your view, but like changes because you want to go to the previous page or the next page, changes this page number to Well, creates a link with page equal to one for the previous page and page equals to three for the next page.

8:36

Speaker 1: Um yeah, so if we give in here previous page one, next page three in this case. We have to copy the original GET request to keep the other query set parameter and then well they can always be none if you're on the first page or if you're on the last page. If we have it, we override it. Well the two by other One if it's the previous page or the two by a three if it's the next page, append it to some dictionary and then we can render it in the template. It's Well, very repetitive code. You have to have this for every view where you need the pagination. You can write such a helper function. Now you can just use the query string template tag. Because what does it do? Well it Also, if you don't change it, keeps the default request. get parameters, so it keeps the path and keeps the especially the query string parameters.

9:24

Speaker 1: So it will only, if we say here Query string page equal to previous page and that will be one. It keeps school A and school B up and only changes it to page equals to three. So like we only have one line of code instead of A whole bunch of function, we don't need any complexity in the view. So yeah, makes your life a lot easier essentially. Um Having talked about views, templates, now we're coming to tests. Um yeah, like you typically you have I mean unit tests, but you also have integration tests where you need the test client. So you would have code like this. where you say okay initialize a client maybe you need to log the user in or something so you say client. first dot login and then you want to send a get

10:11

Speaker 1: request. To set list view, you say, well, and you have to again build the query set parameter yourself. You have to add the question mark, like here is an F string, URL encoded. Okay, great. Well again, now that gets much simpler because the Django test client allows you to explicitly specify the query parameters. You have Less logic in your tests, you can just test the results. That's great because the more logic you have in the test, the more you need to test your tests, and it gets really complicated. Okay, so we talked about URL building. Next one is um protected views. So many times we don't want to build like a public website, but we want to build a

10:58

Speaker 1: website that essentially where the public part consists of a simple Lock-in like a SUS solution or like an internal tool for a company. So all the views will be protected. So the views look like this. We have a Essentially we have to have each view we are defining. We have a login required decorators. We do some stuff. Usually it gets way more complicated than this. You have like a lock-in required Dockerator, then you have to require certain roles, like some permissions you need to check, and even like in this case, um It's again a real-world example. We want to make sure like a certain study group of class is only shown to a teacher from the corresponding schools. If we have something like a required school permissions decorator , While you can test each of these decorators individually,

11:45

Speaker 1: you really want an integration test, especially on the lock-in required, because if you forget it on one view, it's essentially publicly accessible. This became now much easier because there's the lock-in required middleware. It seems like not much because you just add it to the middleware list in your settings subpy. And now instead of views become opt-in for login required, they become an opt-out. So all the views by default are login required. You don't have to care anymore. about integration testing those, so you have to write less code, less testing, because you can rely on Django that makes sure all the views you have in your project. are protected by default. So it's not only less code, it's also safer, which is great. Um yeah.

12:31

Speaker 1: Exactly. So with that, uh yep. Okay, I'm coming to the admin. Um oh Customizing the admin is always fun, right? Um well most of the time it's fun, but like sometimes it's a little bit difficult, like and like it feels like the solutions are in the way. I will give a couple of examples where this became easier. So again, this is our example, right? We have pupils in schools. And well if you click on pupils, well we have a normal list where we say okay we want the name but there is like some additional complexity here we show the school number which is essentially if you remember the models from the beginning foreign key lookup to schools and then like looking up the number and showing this.

13:16

Speaker 1: So how would you do this in Django? Well, the old style way, right? You have to you define an custom model admin, you say list display, you say name, then you say school number, then Django says, hey, what is school number? I don't know what that is. You have to define a property, right? Admin display where you can say ordering and description. And you have to tell Django, okay, look up object. screw, which is the foreign key. dot number and then it knows what this is and shows it to the user and I'm pretty sure many of you have like code like this and like very long admins and it's it's very repetitive Now it just understands under underscore. Nice, simpler, less code, less tests you have to write.

14:01

Speaker 1: Also more consistent with the filter query. So amazing feature. One performance tip here, if you do that, um probably most of you know, but still it's easy to overlook, right? You get three database queries because it actually does this lookup for every single entry in your list. So if you have a hundred pupils, you will get 100 plus 1 lookup because it looks up the pupils and then for each of the entries it will look up the school. So don't forget to add list select related. It's nothing new, but I thought it's a nice tip. Ah because then it does the join and instead of getting a hundred database queries, you get one. Especially is something you don't realize during development, but once you're in production, you have a lot of data suddenly your admin gets slow. Doesn't have to be.

14:47

Speaker 1: Next one, customizable unique constraint errors. So let's suppose Um we want pupils to be not duplicated. Well, it should be identified by the data. So we say, okay, a name can be shared. But if a pupil has the same name and the same birth date, it's probably a duplication, so we don't want to allow it. How would we do that? We would say Unique constraint on name and birth date, which is great. And if we do that, uh the Django automatically generates this error message. Pupil with this name and birth date already exists. Which is also great, it's also translated, but let's suppose you want to customize this. Again, like I don't know, we had a client

15:33

Speaker 1: wasn't happy with this, the admin is great, but like you need to customize it. was really difficult, uh, because you could not access this error message easily. So the best way I came up with was you define a custom form, then you loop over the Errors it produces when you call the superful. clean, you figure it out, you pick it out, and then you kind of override it. It's just n I mean, it works, but it's not nice. Well now you can replace this by nothing and you just uh have to you can add the I mean you could add this before but it was like overwritten my Django now it gets respected you get a violation error message it just works uh so less code easier like you can remove the full form it's really great

16:19

Speaker 1: Yeah. Finally, uh again admin customization, but not only. It's actually not a minor feature now, but like it's still very nice and like helps you in a lot of ways. And so custom bound fields. What's different here from the default is we have this required in brackets. Again, real world example. Yeah, someone doesn't understand that bold means required. And wants to really understand and have it explicitly there. So how would you change that? Um the old way? You probably would need to override the admin template so you can set field sets. then it's also great. Then you have if field because it's special from the Django. field, which is the bound field, dot field, which is the real field you want to know. required.

17:05

Speaker 1: Or you can already see it's it's Probably a bit difficult. It works, yeah? Any of this requires. It only would also apply to the admin. So if you have other forms, you would you can handle this in a different way. Um now you can just um define your It's probably not the best name, but it's a require bound field where you say, okay, this is my custom bound field, where I say, okay, if the field, if the underlying field is required, I change the label adding this required. And then it's the cool thing I can really decide on which level here. Uh I do it on the form level, which I specified in the argument. So I can say For all the fields in this form, use this particular bound field. So it automatically works for all the fields that are required. It gets added. I can also specify this on the project level. So say I want this

17:50

Speaker 1: To apply for all the forms I have in the project, or I can still apply it that was possible before 5. 2 as well on the field level itself and define a custom field with an underlying custom bound field So it really gives you a lot of flexibility. It makes your life much easier. And well, you don't have to hack into any admin templates. So really great. Still uh because it's kind of nice, I wanted to mention it. There's another way to do that in five point zero already, if uh you are not in the atmin you can say, okay, there's custom form rendering that allows you to actually have this intermediate level where you can say, I don't want to actually rewrite how the field is rendered itself.

18:39

Speaker 1: But I want to kind of customize how label field, like the input field and the error messages are changed. So you can register again. On a project-wide level, on a form level, or for the field itself, a custom template for this where you can override , see here like field. label, and you can say field. label. required. add the required brackets and and in uh at the required brackets. This would only work for all non-atmin forms, because admin does their own thing, so they don't respect this overwriting template because they have their own templates. But it's still really nice and in particular I wanted to mention it because if you want to format your form somewhat like labels, it's a really nice way to kind of not have to

19:25

Speaker 1: repeat yourself over and over again because before 5. 0 you really had to specify each field individually to do that. And with that, I'm coming to an end. So we talked about the simplification of URL building in views. So now. If you use reverse, you can explicitly specify the query string as a dictionary and the fragment as a string. Probably remove a lot of custom code to handle this, to format your strings. Less string formatting is always great. You can do the same in templates with the query string template tech, allowing you in particular for pagination to override your query set partially, very nicely, very simple in the template. I mean for client.

20:10

Speaker 1: get and tests you can also now specify query parameters directly, allows you to write simpler tests. In the admin, we should have We saw the lookup, it's just simpler with underscore underscore, and uh how to adjust constraints for error messages. And finally uh for forms we have the bound fields, an example, how to use it And form field rendering. And with that, thanks for your attention. If you want to contact my LinkedIn Thank you.

20:46

Speaker 2: What's your favorite change in the five point X?

20:48

Speaker 1: The favorite change Ah, that's a good question. Um I would say probab um I think the the the query string uh that simplifies pagination is really great. And also the customization of the error message because you're really in the admin because you really can like I mean this was hard before.

21:17

Speaker 3: Hello. Thank you very much for your presentation. Um to the cursor string uh thing uh It's a great thing, but is there a way to skip all the the other uh parameters which are already in the request? Because you uh if I

21:35

Speaker 1: can ex in in the query string template tag you mean? Yeah. Uh you can explicitly set them to none, then they get removed. Okay.

21:50

Speaker 3: Because if you have to set the

21:53

Speaker 1: So I think in the t in the context you provide to the template, you can give it a custom query dict. Usually it uses request. get to allow so that you could do.

22:04

Speaker 3: Okay, yeah.

22:05

Speaker 1: I don't think it's it doesn't make sense really because the it would contradict I don't think you can do it in the template tech itself Uh

22:12

Speaker 4: do you know if this change has been implemented in the source code itself of Django for the simplifications, for example, the forms, no the admin and the testing. Or is this maybe some uh easy pickings for some people to pick up and refactor some of this into the other thing?

22:26

Speaker 1: No, this is all done. Like I mean I showed maybe I mean this is I showed always the version. So if you use the recent version 5. 2, so if you upgrade to the Actually I mean to the recent long-term release you will get all of these.

22:39

Speaker 4: I mean in the actual source of Django code, like does does all the test suit do they use this stuff? Or I know this is not

22:49

Speaker 1: Oh I don't know. I haven't checked. I would guess not all of them, but

22:53

Speaker 4: I mean it might be a massive refactoring for people to do

22:59

Speaker 1: that. To be honest, maybe the Django core test don't use query strings too much because you usually want to avoid those and use like URL parameters. Just like I mean I constructed the example because you sometimes need them So I would guess it's not too many, but I don't know.

23:14

Speaker 4: Yeah, okay.

23:16

Speaker 5: Thank you for the presentation. Uh just a quick question about the the logging required uh required uh middleware. is our login page is automatically exempt from that from that.

23:28

Speaker 1: Yes, I forgot to mention that. I mean obviously Django doesn't make your RAM zone. So um I mean you can explicitly opt out, but like I mean if you I think the login page is uh because otherwise you could not log in as excellent.

23:41

Speaker 5: And what's what's the recommended method for making certain views exempt from that? Is that the Set the authentication classes to to an application.

23:49

Speaker 1: I think there is a newer decorator like uh not requires essentially. Uh uh I think it's named slightly different, but yeah.

23:57

Speaker 5: Thank you very much.

Questions this talk answers

How do I add query parameters and URL fragments with `reverse()` in Django 5.2?

Django 5.2 lets you pass the query string as a dictionary and the fragment as a string directly to `reverse()`, handling URL encoding and missing values for you.

Discussed at 6:10

How do I pass query parameters to Django’s test client?

The test client now accepts query parameters explicitly, so tests can avoid manually constructing and URL-encoding a query string.

Discussed at 10:11

How can I require login for all Django views by default?

Add Django’s login-required middleware to the middleware settings. Views then require authentication by default, with the option to explicitly exempt individual views, reducing both code and the risk of accidentally exposing a view.

Discussed at 11:45

How can I customize Django unique-constraint error messages?

Define a custom violation error message on the constraint. Django respects that message, eliminating the need for a custom form that intercepts and rewrites the default validation error.

Discussed at 15:33

How do I add custom labels or indicators to Django form fields with bound fields?

Create a custom bound field that changes the label—for example, appending an explicit “required” marker—and apply it at the field, form, or project level without overriding admin templates.

Discussed at 17:05

How can I customize the rendering of non-admin Django form fields?

Django’s custom form-rendering support lets you supply templates for a field’s label, input, and errors at the project, form, or field level, avoiding repeated per-field customization.

Discussed at 18:39

How do I remove existing query parameters when using Django’s `querystring` template tag?

Set unwanted parameters to `None` so they are removed; the tag normally starts from the request’s existing query dictionary, which can also be replaced with a custom one in the template context.

Discussed at 21:35

Can Django’s login-required middleware exempt the login page and selected views?

Yes. The login page is exempt so users can authenticate, and individual views can be explicitly opted out with Django’s newer exemption decorator.

Discussed at 23:36

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