An Opinionated Guide to Modern Django Forms with Josh Thomas

This video features Josh Thomas at DjangoCon US 2024 in Durham, North Carolina, USA.

An Opinionated Guide to Modern Django Forms with Josh Thomas
0:37:57
Published December 6, 2024
523 views

Django forms have experienced a significant renaissance within the Django community, after years of being what felt like an afterthought. Recent releases to Django have brought major improvements to built-in form templating and rendering. Instead of writing APIs to support forms rendered by the JS framework of the week, you can now use Django forms to render dynamic, interactive, responsive forms using only the batteries provided by Django, various third-party Django packages, and a handful of small, focused JavaScript utility libraries. And if you're willing to do a little bit of the leg work yourself, you can even get by without those third-party Django packages.

In recent years, web development has started to see a shift away from complex JavaScript-heavy architectures toward more streamlined, HTML-centric approaches. Technologies like HTMX and Unpoly.js enable SPA-like experiences through server-rendered HTML, pairing nicely with Django’s template language. Similarly, libraries like Alpine.js and Stimulus enhance static HTML with dynamic behaviors inline, eliminating the need for comprehensive frameworks. Additionally, CSS frameworks like Tailwind CSS adopt a utility-first approach, simplifying the creation of maintainable CSS.

This shift towards an HTML-centric approach to building web applications is complemented nicely by the recent improvements to Django forms. By leveraging these new capabilities, Django forms can now be used to render forms that are more easily maintainable, more responsive, more accessible, and -- to the end user -- just as interactive as one built using one of the many JS frameworks.

By the end of this talk, attendees will have a deeper understanding of the power and potential of Django forms, equipped with the knowledge to implement them effectively in their projects.

Outline
Introduction to Django forms, including a quick historical overview of where they have come
Exploration of a few new Django form features: template-based form rendering and as_field_group
Styling forms, fields, and errors using modern CSS features, with a focus on Tailwind CSS
Dynamic behavior with minimal JavaScript, with a focus on Alpine.js
Inline validation using AJAX, with a focus on HTMX
Useful third-party Django form packages and why you may not need them anymore
Hands-on demonstration of a simple form using all of the above
This talk will be suitable for anyone who has a basic understanding of Django.

This talk was presented at: https://2024.djangocon.us/talks/an-opinionated-guide-to-modern-django-forms/

LINKS:
Follow Josh Thomas 👇
On Mastodon: https://joshthomas.dev/@josh
Website: https://joshthomas.dev

Follow DjangoCon US 👇
https://fosstodon.org/@djangocon
https://x.com/djangocon

Follow DEFNA 👇
https://www.defna.org/

Video production by Confreaks
Follow Confreaks 👇
https://confreaks.com
https://x.com/confreaks

Summary

Django forms serve both as a validation and data-sanitisation layer and as a presentation layer, which explains their complexity. Josh Thomas outlines a practical progression for modern forms: use native HTML input types and CSS capabilities, customise field and form rendering with `as_field_group` and form renderers, and selectively use third-party tools such as django-template-partials, Django Cotton, HTMX, Alpine.js, and Tailwind. He demonstrates these ideas by building a password-change form with grouped fields, password visibility toggling, inline HTMX validation, current-password checking, and Django password validators, while cautioning that global rendering changes affect every form and that the demo code is illustrative rather than production-ready.

Key takeaways

  • Django forms combine input validation, sanitisation, error handling, and presentation, while widgets control the actual HTML inputs.
  • Native HTML features such as number, email, URL, password, date, color, search, and telephone inputs can provide useful behaviour without custom JavaScript.
  • Django 5’s `as_field_group` and template-based form rendering make it easier to customise field and form presentation while preserving Django’s form APIs.
  • Global form renderers are powerful but risky because changes affect built-in and third-party forms throughout an application.
  • HTMX and Alpine.js can add inline validation and password visibility toggling with very little application-written JavaScript.
  • The demonstrated password form uses Django’s normal validation hooks, including field cleaning and the built-in password validators, but is explicitly a teaching example rather than production code.

Summarised automatically from the transcript.

Chapters

  1. 0:00 Introduction to Django Forms Josh Thomas introduces himself and explains the role of forms as Django’s validation, sanitation, and presentation layer.
  2. 1:56 Django Forms History A brief history of forms in Django, from the framework’s earliest commits through recent rendering improvements.
  3. 5:01 Django Forms Anatomy An overview of forms, formsets, fields, widgets, bound fields, and error handling.
  4. 6:40 Modern Web Platform Inputs Using native HTML input types, date pickers, CSS features, and Django widgets instead of adding unnecessary JavaScript.
  5. 11:17 Custom Field and Form Rendering Customizing field templates with as_field_group and controlling form rendering at the field, request, and renderer levels.
  6. 15:58 Password Reset Form Demo The talk begins building an illustrative password reset form using Django’s modern form APIs.
  7. 17:33 Basic Form Styling and Grouping The demo adds password widgets, custom templates, Tailwind styling, and logical grouping for related password fields.
  8. 20:37 Password Visibility Toggle An Alpine.js-enhanced widget lets users switch password fields between masked and visible text.
  9. 23:42 HTMX Inline Validation The form gains field-level validation with HTMX, template partials, custom events, and MorphDOM.
  10. 28:18 Password and Policy Validation The demo validates the current password and applies Django’s password validators with contextual help text.
  11. 30:35 Further Form Enhancements Josh summarizes the approach and suggests additional improvements such as progress indicators and more targeted validation feedback.
  12. 32:32 Questions Josh answers audience questions about building forms from scratch, integrating Django libraries, and using CSS without frameworks.

Transcript

5,802 words · auto-generated Show

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

0:20

Speaker 1: Hey everyone. Um everyone full from lunch, ready to learn about some Django forms, modern Django forms. That's right. I'm wake you up after after you uh Want to go from go to sleep with a nap. Um like I said my he like he said, my name is Josh. Uh I'm the web develop senior web developer at the Westerveld Company. I've been a web developer, professional web developer for about Four and a half years, so not too long, um, long enough. Um like I said, I work at the Westerveld Company. We're a lumber land company, we own a bunch of Timberlands, we own land and try to use it in a responsible way. And Django is a big part of that. We have a lease application that manages our hunting leases. Among many other things, that there are links, we have uh our main website and then our GitHub profile where we actually have a bunch of open source.

1:11

Speaker 1: um Django libraries mainly for my sake so I don't have to re keep on rewriting things over and over again but hopefully maybe something y'all will find useful. Okay, so what are forms? What are forms? That's what we're going to talk about today. Well, as far as HTML is concerned, in the web, they're one of only two elements that actually kind of can interact. with a server built into the the platform, the other one being an anchor tag. Forms are the only ones that actually take user input and allow a server to respond. So what are forms, but what are forms in Django? Well they're primarily a data sanitation and validation. and presentation layer. They kind of play a dual role.

1:56

Speaker 1: And it's kind of why they're so complicated. They take user input, sanitize it, make sure it's it's Safe to store or it matches certain validation rules and then also presents the form to the users for them to interact with. First question is, how long have forms been in Django? Does anyone know? I know some some old gray bears down here know. Um well the answer is uh pretty much since the beginning. Uh I went back and found it's the second public commit on the GitHub. uh repo um made by Adrian. Um there 's the the link the actual link to the commit and then the um permalink to the Django core form

2:42

Speaker 1: fields that's what they were called then um This is the only public link I could find of them. Before that it was in a SVN repository, I think. I was talking with Jacob Catlin-Moss and he said pretty much since the beginning, forms have been in Django because they're such an important part of the web. Here's a few kind of notable changes to Django forms over the years, and uh I just kind of combed the release notes in 0. 95. That's kind of the first available release on GitHub. The forms were included in 2006. In 2007, something called Django New Forms, which is actually the Django forms we know today pretty much. 1. 6 added uh GeoJengo

3:27

Speaker 1: form widgets. 1. 7 was a big form validation release. Form. add error was added then and just a bunch of validation stuff in that. uh 1. 11 in 2017, added template-based widget rendering. Um, and then there was kind of a wide gap between uh major features to the form library uh up until 2021 in 4. 0 when we got actually template-based form rendering. So previously it was just widgets And uh in 4. 0, the entire form library uh was rendered by a Django template language. Um it also introduced the form renderer. class uh that was responsible for that. 4. 1 brought uh the div-based form templates um as well as being able to customize your form

4:16

Speaker 1: template name, your form, your default template on your form render, and then a 5. 0 brought as field group, which we'll get into later. I put on my data science hat to do this. This is kind of a count of the commits over the years, since 2005 up to this year. kind of showing you the different modules, main modules. We got contrib admin, we got auth, sessions, chord, db is in there Uh and the form is just kind of right there in the middle. Um you can kind of see it. It's never been the most popular thing to contribute to. Um but it's kind of just always been there and just kind of always um in the background. You can also see I know these are hard to

5:01

Speaker 1: this is hard to read. There's new forms and old forms are down there as well as a reference to where they came from. Okay, so what parts make up Jenga forms? Well these are kind of the core bits. We got, you know, the form is kind of the central piece of the puzzle. It's responsible for your validation and your rendering of just all the fields. on the form. Your form set is just multiple form instances. And is it a pain to deal with, which I will not be going into. Fields are forms, but just on a single field level, they're responsible for validation and rendering of a single field. The widget is the thing that actually renders the input to the users. Your bound field is a combination of field and data, and that's either the user-created data, user-supplied data, or the initial data that you give it.

5:54

Speaker 1: when you instantiate it. And then there's also error list and error dict, and those just kind of handle all the errors in your form and associate it field with the errors that go along with it. Um and uh to me when I was learning Django, this kind of all seemed like a humongous puzzle. It doesn't seem like a lot, but how they fit together. in the different layers um can make you kind of feel like this when you're going through and I feel like that and I've been preparing for this talk for a while um I still feel like that because forms are complicated. So the form library is kind of complicated. But it's complicated to hopefully save us time when we're building our Django apps

6:40

Speaker 1: Okay, so now we're going to go through and talk about a little bit about leveling up your Django forms with a couple modern additions, new additions. We're going to go through four levels. First up, this is an easy one. Use the platform. Use the platform that we we um we all use, the web platform. What does this mean? Of course it's all the input types that we kind of just get out of the box with HTML. You type number, email, URL, and password. Those are kind of some of the most common ones and you know how that looks is everyone knows this. You have a number field, you can you can't I'm I'm typing in letters it's not letting me no JavaScript, no nothing It will let me do numbers.

7:25

Speaker 1: Send your email. It'll validate that it's a valid Uh valid email. URL, same thing, and a password will mask. So Jim from accounting can't spy on your password. And how this looks, of course, in Django is is is pretty simple. We have our form widgets and there's a number input, email input, URL input, password input. And if you use model forms, um They come out by default with the integer field. You do have to specify localize equals false. Otherwise it uh renders to a text input. But if you specify that, then you get a number input. provided by the library.

8:11

Speaker 1: There's also a handful of less commonly used or less commonly known. I feel like I rarely see these. um your color, your search, and your tell, your telephone input types. And how you would use them in Django is is similar to how you define a custom widget. And that is a Inheriting from widgets. input and specifying the different input types for color, for search, for tell. And this interestingly, this stuff is coming in the next release, I believe, Django 5. 2. I think I copied this directly from Maine when I was creating these slides, but you can use these today. And how they look is Like this, you get a color input with a nice

8:57

Speaker 1: wide gamut of all the different colors that you can choose. And it's platform specific. So if I pulled this up on my phone, it would render the actual iOS color input. Same for search and for tell it'll bring up the number pad and a list of local numbers that you're using. There's also date type um date input types. Um those are a little bit trickier because in HTML there they have no time zone data. Well, we have to handle handle time zone data, otherwise we get uh those weird bugs. Um there are a handful of track tick tickets um on the Django

9:42

Speaker 1: ticket tracker. discussing this and and it's been brought uh brought up that maybe Django should have this built in to the form library. Um I don't know if that's ever gonna happen. I mean it's it's You gotta handle the time zone somehow, but um if you wanted to do it yourself, um this is how you would do it, and then you get your nice little date picker without any JavaScript That's gonna be a theme. Um I threw this in at the last minute. Has I don't does anybody know the CSS property has? It was just it's kind of been around for a little bit, but it's been broadly available this year. Is anyone familiar with it, used it? Yeah, um has is great. Um I'm not gonna go into any detail beyond uh

10:28

Speaker 1: acknowledging the potential of it, especially within the Django Forms library where you perhaps want to act on uh a custom widget that you don't want to you don't want to copy that custom widget template into your own project because then you've got to maintain it you've got to think about it later on When a library updates it, you gotta update it yourself. Um has allows you to style a parent element based on a child element 's attributes, anything, state, um, data attributes. Um so I've just begun to play with has and I encourage you to as well. So that's use the platform, that's level one. Level two is the ASField group introduced in Django 5. 0 And that allows you to define custom templates for your Django fields.

11:17

Speaker 1: You can do it a couple different ways. You can do it as a uh attribute, uh sorry, a passed in attribute, a passed in variable to your uh when you instantiate, define your uh Django field and template name. You can also do it at the request level and have it render a different template. You can also do it at the form renderer level and have a global custom field template to rule them all And then then your form template, uh, you have to adjust it a little bit. You have to call your form. field dot as field group, but That's all you got to do and then depending on how you define that template name The template just works.

12:03

Speaker 1: So that's field as field group Next up form render and it's well it's what as field group is for forms, and it's defined very similar. You can define a custom template name on a form, on a request and at the template level or excuse me the form renderer level. You can also set a global form renderer to a custom one. I would generally uh not advise again uh not advise us. Um that's this is the way I've did it at the beginning of the year. And um you can ask Jeff Triplett. He uh has had many headaches because of that decision. This is a big hammer and um you should wield it responsibly. If you change something in this template

12:49

Speaker 1: It's going to apply to not only any form that your application is using, it's going to apply to any third party that's relying on the form template library, or excuse me, the form rendering library. I made a change to add a required asterisk, a little red asterisk to help my users out and uh I started seeing red asterisk everywhere in the debug toolbar and the admin, everything uses this. If you do this, use use it responsibly. So that's form renderer. Next up, the last one is uh Maybe don't use Django forms, and I'm obviously being a little facetious there. Maybe don't use Django forms only. Um and this is Probably pretty common practice. We have a bunch of awesome third-party libraries.

13:38

Speaker 1: Django Crispy Forms obviously is the most popular one. There's Django widget tweaks as probably equally as popular. There's an interesting one that I've been wanting to play with, Django FormSet. It is kind of a rethink of the Django temple library and does it in its own way and it allows for grouped fields and grouped form sets. Super interesting. There's also some template-based component libraries. Carlton's Django template partials is great, and you're going to see some later. I know Django Components and Slippers are both two very popular template rendering libraries. I wanted to bring attention to Django Cotton. That's kind of a brand new one. As of, I believe, nine months ago,

14:26

Speaker 1: it kind of reimagines what Django templates can look like. instead of using your curly braces and your percent signs and your typical Django template goodies. you um instead use that anacotton dot or excuse me cotton slash input dot html in there you use all your familiar Django template variables and control methods and then in your actual template use this um kind of funny syntax that kind of makes it look like a custom form or excuse me custom web element um c dash input You can have slots, default variables like the type text, or a leading icon and trailing icon.

15:11

Speaker 1: And so you end up kind of getting a nice little uh form input right down there with the the leading icon. Okay, so those are the four levels. Use the platform, use the inputs, use CSS. CSS is great. and is getting better every day. The field as field group. We got our form renderer and then you know all the additional third-party application, third-party libraries provided by the Django ecosystem. Okay, so let's go ahead and build something with all this kind of knowledge that we've just gained. I'll show you what we're gonna build. We're gonna build a a a password reset form um deceptively complicated.

15:58

Speaker 1: So we're going to have a current password, obviously. We're going to have a new password with a validation. And then we're also going to have a toggle for to display change between type password and type text. And so how that looks You know, everyone's seen this. Everyone's seen a password reset form , except this one has inline validation using HTMX. If I do this, I got one, two, three. That's a numeric password. I go away. It validates against my uh password validation rules in my Django project. Yeah, so that's what it's gonna look like at the end. It's got fill required as well. No refreshing, no JavaScript. minus uh a handful of libraries.

16:44

Speaker 1: Um I do wanna add a disclaimer I built like ninety percent of this and then I realized It has a humongous security bug, which I'm sure someone will point out to me and make fun of me later. So don't use this, please. This is just an illustrative purpose. um kind of give you an idea of the different APIs that are now available to the Forms Library. Form library was um pretty static for a while. And then within the past year, it's it's it there's got a new life. There's there are new APIs, new, new goodies to play with. And also say like the reason for the title of this talk is this is an opinionated um guide. This is just one way to build a form like this. Um it's not necessarily one I would agree with in four months.

17:33

Speaker 1: Like I said, th these APIs And these techniques are kind of new-ish. And we're just trying to feel our way out, find the right abstractions, find the right patterns. Okay, so let's get started. We need our current password, new password one, new password two. We're just going to start out with char fields, define the form, use the generic form view. Create our template. Boom. We're done. Right? Zip. We got our password reset. Except. Like I said, if Jim from Accounting comes across and he's going to capture your password. So let's go ahead and use the platform Use those custom widgets, forms. passwordinput. That'll

18:18

Speaker 1: set that input type to type password. And then Jim doesn't know anything. Okay. Um except it's, you know, it's kind of looking a little plain. Let's jazz it up. People like to use nice looking inputs and forms. So let's jazz it up a little bit. Let's move our, let's create a custom um Custom password feel, um go ahead and inherit that or add the uh forms. password input widget as our custom widget And then we'll go ahead and say if if we don't pass in a template name in when instantiating the field, we'll go ahead and set a default one. This kind of allows us to adjust it later if we like.

19:04

Speaker 1: And then that custom widget template kind of looks like this, a standard uh Django widget template. We got our field. label with some nice little tailwind CSS. We got our field errors. We got our actual field and the help text. Everyone knows this. So then we change our uh char fields to our password custom password field. Um and then we're going to go ahead and add some nice uh different tailwind styles to the the input, make it look nice, give it a nice placeholder, a nice uh border when it's highlighted, when it's focused, excuse me. Now we're gonna go ahead and adjust our submit button as well, make it look nice as well.

19:52

Speaker 1: Now this, this is a form, this is what I'm talking about Now this I can use every day. Except, let's see. Except I don't really like new password one, new password two, that doesn't make sense. Those form fields go together. They're kind of a form group. So we're going to change that. We're going to go ahead and set the labels to be default as blank strings. Get rid of them. Throw them out of here. And then we're going to call our as field group. We're gonna go ahead and keep the current password the same. That one was fine for now. And then we're also then we're gonna create a new field set with a legend of new password, and that's gonna be the label.

20:37

Speaker 1: And then we're gonna Use a grid and do our new password one and two as field group. And now look grouped together. This looks nice. This looks nice, usable, logical. It makes sense that they're together. Except I don't I don't remember what I just typed now. Is that my current password? I don't remember. Wouldn't it be nice? If we had like a toggle to go between, nice little eye icon to make sure I knew that I was typing the right password. So uh we'll change our template, our widget template um to a custom widget template at password input. Um Then this one starts to go a lot hairy. I hope you guys can follow.

21:23

Speaker 1: I'm gonna do my best to explain it. Um we're using Alpine, Alpine JS. Um it's a nice little simple. Um JavaScript library for adding attributes to an HTML element that give it the nice interactivity that we all like with modern applications. And you get to brag that you don't actually write any JavaScript. You just rely on a library. And so we're going to start by setting the outside container with an X data of hidden equals true. And so that means if it's hidden, it's a password input. If it's not hidden, we're just going to change it to a text input. and make sure we can actually see it. So we make sure we typed in the right password. So we'll go down and we'll actually show the actual input. We're going to do a bind

22:09

Speaker 1: is what an Alpine speak is what you call that colon. We're going to bind the type to the hidden variable that we just set right here. So if it's hidden, we're going to set the input type as password. Otherwise, if it's not hidden, we're gonna change it to text. And I believe those classes have not changed, so we'll move on. And then uh we're gonna add our button. button on the edge and we're going to say when we click it we're going to toggle between the two different states, hidden or not hidden. We're going to use Adam Johnson's excellent Heroicon 's template library, or excuse me, Icon library. And depending on whether it's hidden or not, we're going to show an I slash with a slash to it or just the open eye.

22:56

Speaker 1: And that X show is doing that right there. And so that's kind of how it all looks put together. Yeah, so how we doing? How we doing? Yeah, I'm liking this. This is feeling pretty good. I'm feeling like I can really change my password a lot of times now. That was a little scary. Now we're gonna really get to the the the the hairy parts. Um and I'm going to lean on Luke Plant's excellent Django HTML patterns repository, not only talking about forms, but it's got a bunch of patterns that we can use in our Django applications when using HTMX.

23:42

Speaker 1: And this what I'm about to show you is kind of inspired by his, there's a form validation documentation in there. It's kind of inspired by that. Well first up we need to pop that uh those field groups out as a uh template partial um because we're going to do some inline validation. So we're going to load our Django template partials. We're going to define the field partial up there. And if you've never used HTMX, um This is a lot of variables or a lot of attributes, excuse me. But it's pretty easy to follow. Like the HX git just means we're gonna issue a Git to the current URL. when this is triggered. We're going to pass in the the field name as the validate field in the request.

24:27

Speaker 1: We're going to make sure to trigger it from a custom event um password blur which we'll get to in the in the next slide um and we're going to save the password blur event from the container of the widget container, excuse me. We're going to include the entire input. We're going to target this specific field partial template. And then we're going to use the Morph DOM. um Django or excuse me, MorphDOM HTMX extension because it uh handles since we're using inputs and we're gonna have focus states, um the MorphDOM library, the Morphenom HTMX extension, excuse me, um handles that focus state pretty good, pretty well. Okay, so then down in our form we just kind of swap the as

25:14

Speaker 1: field group and use the Django, excuse me, the Django template partials, Django partial partial templates that we just defined using those field. And then here's where it gets a little scary. Previously we just had hidden true because we have an input with A text input in a in a button that's going to toggle between password and text. Well, we've got to figure out how to handle that. W we couldn't just use if the the input, the text input le loses focus. Um Because then when we tab to the the hidden button, it would validate and lose our focus and throw the user off. So we're going to handle that somehow. So we're going to set a new

26:01

Speaker 1: variable has focus, we're going to set it to false, and then we're going to have a handler that basically says if we lose focus, trigger dispatch, this custom event password blur. And then on the two inputs, excuse me, the input and the button, we're going to do the axon event and we're going to save If we focus on the input or the button, it's true. And if we blur or we go away from the input, we're going to set this the focus as false. And then on the next tick, the next rendering cycle, we're gonna go ahead and call that handler. And if both of our inputs, or excuse me, if if our input and our button have both lost focus, then we'll just trigger that, dispatch

26:47

Speaker 1: that event. We do need to adjust our fit our form view though. As a reminder, this is what it looked like. Very simple. It's about to get more complicated. We're going to override the get method, and we're going to check if the request has an H is an HTMX request using Adam Johnson 's Django HTMX package. We're also going to check for the GET request for that validate field variable that we set in the request. And if we have both those things, we know we're trying to validate that specific input. So we're going to pass it into the form class, we're going to check if it's valid, and then we're going to get the bound field from the

27:32

Speaker 1: form data, form clean data. And then we're going to render the form using a custom template with the partial using the fragment, the hash, the number sign. We're going to target that template partial that we defined earlier, the field partial. I know that's a lot to follow, especially in uh talk slides, um, but basically it just means that. If I focus on this, it's fine, but if I go away , feel is required. Doing that inline validation. But if I type something, go away, it says it's valid. And if I go away like that and kind of just go around. pop around and it just kind of just works.

28:18

Speaker 1: It's a little bit hairy to get there, but it just kind of works. And still no lines of JavaScript that we have written personally. Okay, so that's the that was kind of the hard part. Now we're gonna kind of spice it up a little bit or put some sugar on top. Let's check if the current password that is uh given to us matches what the user's password is. Um Since this is a talk demo, I'm just going to hard code it to DCUS 2024 , just so we can prove that it works. And that's all it takes is just on the form overriding the clean current password method and if it doesn't match we'll return a validation or raise a validation error

29:04

Speaker 1: and now if I don't type You know, you can see not DCU DCUS 2024 does not match current password. That's just what we want. But if I type in 2024, we're all good. We're all good. Okay, I believe this is the last step. Um you know, I'm liking where we're going so far. However, on the new password, as a user, I don't know what expectations we have. So let's add some help text as well. So we're going to import from Django Contriboth, the password validation module. And then set the help text to the password validators, help text HTML, and then also

29:49

Speaker 1: throw a new clean method for a new password, grab it from the clean data, and pass it through the password validation. And so now this is great as a user. I know exactly what to expect. Except if I do so if I do a single number, comes back with my validation in line. I think that is it. Yep. So I'm gonna stop there mainly because I ran out of time. But there are plenty of ways we can just keep on iterating on this, like instead of the help text, excuse me, instead of the validation text being on the top, we could try to swap the help text.

30:35

Speaker 1: in place and so you know exactly which ones uh you have yet to do. You can add a a progress bar to see how how uh long it is, what how close to eight characters you are. um kind of the mine reels. Um and so that's it. That's uh that's my thought um I thought I w I went a little too fast. I was excited. I'm excited. This stuff stuff gets me excited. Yeah, that's kind of it. I can make more slides up, but it's like y'all don't want to sit there and watch that. Um so my the talk slides

31:21

Speaker 1: will be on my personal website. Um Later today. I was busy building those forms to do that. So that QR code actually doesn't even work. But it'll be there. Joshtomas. dev. slash talks. You can find me on the different places. There's my personal website. I'm also on GitHub and I have a Mastodon and I'm also on Blue Sky. I'm giving it a try. So that's it.

31:57

Speaker 2: How long it took you to find the the comet in the fly.

32:03

Speaker 1: Uh probably 20, 30 minutes. Like I can even remember. I started by clicking back a couple times on GitHub and that didn't work. And um I think I wrote a little small little script and using Git and just find me the oldest commit on here. The first commit is actually um um documentation. The the actual code comes from the second public commit

32:32

Speaker 3: All right. This one comes from an online attendee. Uh says, I'm curious, Josh. In your example you were building a form from scratch. Was that for demo purposes? Or do you have you run into trouble integrating this these techniques into your pre- the Django pre-built form classes in the past?

32:45

Speaker 1: I mean it was mainly for for the talk purposes, for demonstration purposes, kind of to show you beginning to end. You wouldn't do all those kind of steps. I don't do all those steps, but I was trying to do my best to show the as field, as field groove , being able to customize the rendering. kind of throughout it. Like no, you're not going to use that for every single form, uh every single application, um, but it's there and it's not uh too bad and it's getting better. Um it's getting better every every release.

33:18

Speaker 4: Thank you very much. Um you mentioned libraries by Adam Johnson, Luke Kant. So it sounds like you're you have at your disposal a toolbox of uh different components. What was the experience of integrating them as a unified Uh pieces of software that seem to think in the same way.

33:43

Speaker 1: Well, hmm, that's a good question. Um they are, I mean, it just depends on what layer in the application they're kind of working with. Um Django template partials and Django Cotton for example example both use custom um template loaders So getting that integrated together, getting it in the right order. Luckily both libraries are very excellent and they have good maintainers. And they have good they have good um escape hatches. Like uh they're they're they they offer automatic setup of the template loaders um freeze of use, but also have escape hatches for for manually setting all that up. So it kind of just depends on, you know, like

34:29

Speaker 1: those are two temp two specific template loader libraries. But if you're using like uh Django HTMX and Django Cotton or Django Temp Partials or Django uh widget tweaks. Like those are taking care of two different things. So um Yeah, you didn't have it.

34:55

Speaker 4: You didn't have problems with them fighting with each other at any point.

34:58

Speaker 1: No, not really. Yeah. I mean it was it was uh pretty pretty painless, pretty seamless. Hopefully a nice um user experience as well So

35:14

Speaker 5: my question comes from the use of uh regular s or just custom style sheets. Say you want to venture away from CSS frameworks. What would you actually recommend to implement something like that? You know, would you use your typical mod or like a different package or would you just use Raw Django?

35:36

Speaker 1: Are you talking so you uh let me repeat the question back just so I make I understand? You're not talking about you're talking about moving away from CSS entirely or using a uh

35:48

Speaker 5: I'm just talking about not using something like boot bootstrap or tailwind and using purely CSS and implementing your own.

35:56

Speaker 1: I'm the wrong person to ask. I'm deep on Tailwind. I love Tailwind. Um it may it helps me write CSS. um these days. And it's it's extendable, it's getting fast. A pattern I like with Tailwind now is is to, you know, still fall back. Two CSS defined in a CSS file, um, but have Tailwind process it. So um You can still access your theme, your nice um design package. That's kind of the the the the utility classes are an you know a nice uh addition, but really the theme, the the design patterns Especially as someone who is design challenged. I like leaning on that.

36:42

Speaker 1: I tend to just stick with that and not fall back to raw CSS. But I mean Raw CSS is great these days. And it's like I said, it's getting better. Has just came out. There are container queries. I mean, the list goes on and on. You don't even need SAS or C S CSS these days, really. So I hope that answered your question. I'm sorry. I'm t I'm I'm all in on Tailwind, so that's the answer. That's the answer.

37:18

Speaker 2: Any more questions? Oh well uh Thank you, Josh, and uh big round of applause, please.

Questions this talk answers

What are Django forms used for?

Django forms act as both a data-sanitization and validation layer and a presentation layer. They accept user input, check it against rules, and render fields for users to interact with.

Discussed at 1:11

What are the main parts of Django’s form system?

The core pieces are forms, formsets, fields, widgets, bound fields, and error lists or dictionaries. Forms handle validation and field rendering, while widgets render the actual inputs and bound fields combine fields with data.

Discussed at 5:01

How can I use HTML input types in Django forms without JavaScript?

Use Django’s built-in widgets such as number, email, URL, and password inputs, or define a custom widget with the desired HTML input type. HTML then provides features such as numeric and email validation, password masking, and platform-specific controls.

Discussed at 6:40

How do I render custom Django form fields with `as_field_group`?

Django 5.0’s `as_field_group` lets you provide custom templates for fields, either on an individual field, per request, or through the form renderer. After defining the template, render the field with `form.field.as_field_group`.

Discussed at 10:17

How should I use Django’s form renderer safely?

You can customize the form template at the form, request, or renderer level, but a global renderer affects every form in the application, including third-party forms, the admin, and the debug toolbar. The speaker recommends treating that global change as a powerful tool and using it cautiously.

Discussed at 12:03

What alternatives to built-in Django forms can I use for custom form rendering?

The talk highlights third-party options including django-crispy-forms, django-widget-tweaks, Django Formset, django-template-partials, Django Components, Slippers, and Django Cotton. These libraries provide different approaches to styling, grouping, and composing form fields and templates.

Discussed at 13:28

How can I add inline validation to a Django form with HTMX?

Render each field as a template partial, submit the field name with an HTMX request when the field loses focus, validate that field in the form view, and return only the corresponding partial. Alpine.js is used to avoid triggering validation while the user moves focus between the password input and its visibility button.

Discussed at 23:42

How do I validate a current password in a Django form?

Override the form’s `clean_current_password` method, compare the submitted value with the user’s actual password, and raise a validation error when it does not match.

Discussed at 28:44

How do I apply Django’s password validators and show password requirements?

Use Django’s password-validation module to generate the password requirements as help text, then override the new-password cleaning method and pass the submitted value through the configured validators. Validation errors can be returned inline through the same HTMX pattern.

Discussed at 29:09

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 by Josh Thomas

More videos from DjangoCon US