2024-12-06 | DjangoCon US | Opinionated Guide to Modern Django Forms by Josh Thomas
现代Django表单实践:整合平台能力与第三方工具打造卓越体验
标签
媒体详情
- 上传日期
- 2025-06-26 23:18
- 来源
- https://www.youtube.com/watch?v=BeWGag58PVA
- 处理状态
- 已完成
- 转录状态
- 已完成
- Latest LLM Model
- gemini-2.5-pro
转录
speaker 1: Everyone, everyone full from lunch, ready to learn about some Jango forms, modern Jango forms. That's right. I'm wake you up after you want to go from, go to sleep with a nap. Like I said, like you said, my name is Josh. I'm the senior web developer at the westvilt company. I've been a web developer, a professional web developer for about four and a half years. So not too long, long enough. Like I said, I work at the Westervelt company. We're a lumber Land Company. We own a bunch of timberlands, own we own land and try to use it in a responsible way. And Jango 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 our main website and then our GitHub profile, where we actually have a bunch of open source Jango libraries, mainly for my sake, so I don't have to 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, there one of only two elements that actually kind of can interact with a server built into 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 Jango? Well, they're primarily a data sanitation and validation and presentation layer. They kind of play a dual role, and it's kind of why they're so complicated. They take user input, sanitize it, make sure 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 jangos? Anyone know? I know some old gravers down here now. Well, the answer is pretty much since the beginning. I went back and found it's the second public commit on the GitHub repo made by Adrienne. There's the link, the actual link to the commit, and then the permalink to the Jango core form fields. That's what they were called then. 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 Kat lamoss, and he said, pretty much since the beginning, forms have been in Jango because they're such an important part of the web. Here's a few kind of notable changes to Jango forms over the years. And I just kind of combed the release notes in 0.95. That's kind of the first available release on GitHub. Forms were included in 2000 62007, something called Jango new forms, which is actually the Jango forms we know today. Pretty much 16 added geojango form widgets. 107 was a big form validation release. Form add error was added then, and just a bunch of validation stuff in that one. 1:11 in 2017 added template based widget rendering. And then there was kind of a wide gap between major features to the form library up until 2020 14.0, when we got actually template based form rendering. So previously it was just widgets in fort auto. The entire form library was rendered by jgo temple language. It also introduced the form render class that was responsible for that. 4.1 brought the div based form templates as well as being able to customize your form template name. You form your default template on your form render, and then a 5.0 aspfield group, which we'll get into later. I put on my data science hat to do this. This is kind of an account 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 off th sessions. Core dv is in there. And the forms is kind of right there in the middle. You can kind of see it. It's never been the most popular thing to contribute to, but it's kind of just always been there and just kind of always in the background. You can also see, I know of these, it's 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 jinga 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 you're 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 bfield is a combination of field and data, and that's either the user created data, user suppdata, or the initial data that you give it when you instantiate it. And then there's also erlist and air 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. And to me, when I was learning Jango, 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 can make you kind of feel like this when you're going through. And it I feel like that, and I've been preparing for this talk for a while. 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 Jango apps. Okay, so now we're going to go through and talk about a little bit about leveling up your Jana 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 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, your 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 filled. You can't. I'm typing in letters. It's not letting me. No JavaScript, no nothing. We'll let me do numbers. Send your email itvalidate that it's a valid, valid email url. Same thing. And a password will mask. So jump from counting. Can't spy on your password. And how this looks, of course, in jangos 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, they come out by default with integer field. You do have to specify localize equals false, otherwise it renders to a text input. But if you specify that, then you get a number input just provided by the library. There's also a handful of less commonly used or less commonly known. I feel like I rarely see these. Your color, your search and your your telephone input types and how you would use them in Jango is similar what to how you would define a custom widget. And that is inheriting from widgets that input and specifying the different inpetites for color, for cerx, for tell. And this, Interestingly, this stuff is coming in the next release, I believe Jango 52. I think I copied this directly from main ine 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 y 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, itbring up the number pad and a list of local numbers that you're using. There's also date input types. Those are a little bit trickier because in html they have no time zone data. Well, we have to handle times on data, otherwise we get those weird bugs. There are a handful of track tickets on the Jango ticket tracker discussing this, and it's been brought brought up that maybe Jango should have this built in to the form library. I don't know if that's ever gonna to happen. I mean, it's you got to handle the time zone somehow, but if you wanted to do it yourself, this is how you would do it. And then you get through a nice little date. Picwithout, any JavaScript that's going to be a theme. I threw this in that 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. Has anyone familiar with it, used it? Yeah has this great. I'm not going to go into any detail beyond acknowledging the potential of it, especially within the Jango forms library where you perhaps want to act on a custom widget that 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 got to update it yourself. Has allows you to style a parent element based on child elements, attributes, anything, state data attributes. So I've just begun to play what has and I encourage you to as well. So that's use the platform that's level one. Level two is the Ashfield group introduced in Jango five zero. And that allows you to define custom templates for your Jango fields. You can do it a couple different ways. You can do it as a attribute, sorry, a past, then attribute a fast end variable to when you instantiate, define your Jango field 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, you have to adjust it a little bit. You have to call your form field 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. 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 render level, you can also set a global forrender to a custom one. I would generally not advise this. That's this is the way I did it at the beginning of the year. And you can ask Jeff triplet. He has had many headaches because of that decision. This is a big hammer, and you should wield it responsibly. If you changed something in this template, 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, red asterisk, to help my users out. And I started seeing red asterisk everywhere in the delutoolbar and the admin. Everything uses this. If you do this, use it responsibly so as form renderer. Next up, the last one is maybe don't use Jango forms, and I'm obviously being a little facetious there, maybe don't use Jango forms only. And this is probably a pretty common practice. We have a bunch of awesome third party libraries. Jango crispy forms, obviously is the most popular one. There's Jango widget tweaks is probably equally as popular. There's an interesting one that I've been wanting to play with, Jango form set. It is kind of a rethink of the Jango temple library and does it in its own way. And it allows for grouped fields and group form sets. Super interesting. There's also some template based component libraries. Carlton's jgo template partials is great, and you're going to see some later. I know jgo components and slippers are both two very popular template rendering libraries. I wanted to bring attention to Jango cotton. That's kind of a brand new one as of, I believe, nine months ago. It kind of reimagines what Jango templates can look like. Instead of using your curly braces and your percent signs and your typical Jango template goodies, you instead use that anaccotton dot or excuse me, cotton slash input html in there, you use all your familiar Django template variables and control it's and then in your actual template, use this kind of funny syntax that kind of makes it look like a custom form or, excuse me, custom web element sedash input. You can have slots, default variables like the type text or a leading icon or trailing icon. And so you end up kind of getting a nice little form input right down there with the leading icon. Okay. So those are the four levels. Use the platform, use the inputs, use css. Css is great and it's getting better every day. The field is field group. We got our form renderer. And then you know all the additional third party application, third party libraries provided by the Jango 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 going to build. We're going to build a password reset form, deceptively complicated. 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. Validates against my password validation rules in my Jango project. Yeah. So that's what's going to look like at the end. It's got fill required as well. No refreshing, no JavaScript, minus a handful of libraries. I do want to add a disclaimer. I built like 90% 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. Kind of give you an idea of the different apis that are now available to the forms library. Form library was pretty static for a while. And then within the past year, there's got a new life. There are new apis, new new goodies to play with. And also say like the reason for the title le of the stock is this is an opinionated guide. This is just one way to build a form like this. It's not necessarily one I would agree with in four months. Like I said, 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 charfields, define the form, use the generic form view, create our template. Boom, we're done right. Zip. We got a password reset, except, like I said, 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 that password input thatI'll, set that input type to type password. And then Jim doesn't know anything. Okay, except it's you know it's kind of looking on the plane with 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 custom password feel and go in and hear it that or add the forms that password input widget as our custom widget and then we'll go and say if we don't PaaS in a template name when instantiating the field and set a default one, this kind of allows us to adjust it later if we like. And then that custom widget template kind of looks like this standard Jango widget template. And we got our field label with some nice little tailwind css. We got our field harors. We got our actual field in the help text. Everyone knows this. So then we change our char fields to our custom password field, and then we're going to go ahead and add some nice different tiwind styles to the input, make it look nice given it a nice placeholder, a nice border, when it's highlighted, when it's focused, excuse me. Now we're going to go ahead and adjust our submit button as well, make it look nice as well. 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 didn't make sense. Those form fields go together. They're they're kind of a form group. That's I just it just I doesn't run b me, right? So we're going to change that. We're going go 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 going to go ahead and keep the current password saying that one's fine for now. And then we're also then we're going to create a new field set with a legend of new password, and that's going to be the label. And then we're gonna to use grid and do our new password 12 as field group. And now look group together. This looks nice. This looks nice. Usable, logical. It makes sense that they're together, except I don't remember what I just typed now. Is that my current password? I don't remember. Wouldn't it be nice if I had like a toggle to go between nice little eye icon to make sure I knew that I was typing the right password? So we'll change our template, our widget template, to a custom widget template at password input. Then this one starts to go out here. Ry, I hope you guys can follow along. I'm going do my best to explain it. We're using alpine alpine js. It's a nice little simple 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 run a library. And so we're going to start by setting the outside container with an x data of hidden it equals. And so that means if it's hidden, it's a password input. 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 can make sure we typed in the right password. So we'll go down and we'll actually show the actual input. We're gonna to do a bind is what an alpine speak is, what you call that cone. We're going to bthe 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 going to change it to text. And I believe those classes have not changed. So we'll move on. And then we're going to 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. We're not hidden. We're going to use Adam Johnson's excellent heroic con's template library, or excuse me, icon library. And depending on whether it's hidden or not, we're going to show an eye with a slash, do it or just the open eye. And that x shows is doing that right there. And so that's kind of how it all looks put together. Yeah. So how are we doing? How are 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 going to really get to the hairy parts and I'm going to lean on loop plants. Excellent Django html patterns repository. Not only talking about forms, but it's got a bunch of patterns that we can use in our Jango applications when using htmx. 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 those field groups out as a template partial because we're going to do some inline validation. So we're going to load our Jango template partials. We're going to define the field partial up there. And if you've never used hdmx, this is a lot of variables or a lot of attributes. Excuse me, but it's pretty easy to follow. Like the hx get just means we're going to issue a get to the current url. When this is triggered, we're going to PaaS in the field name as the validate field in the request. We're going to make sure to trigger it from a custom event password blur, which we'll get to in the next slide. 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 morphdom Jango, or excuse me, morph dom htmx extension because it handles since we're using inputs and we're going to have focus states, the morphdom library, the morphdom htmx and excuse me, handles that focus state pretty good, pretty well. Okay. So then down in our form, we just kind of swap the aspfield group and use the Jango, excuse me, the Jango temple partijango partipartial templates that we just defined using those field. And then here's where it gets a little scary. Previously we just had hidden because we have an input with a text input in a button that's gonna to toggle between password and text. Well, we got to figure out how to handle it. We couldn't just use if the input, the text input loses focus because then when we tab to 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 variable has focus, we're 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 Exxon event. And we're going to say, if we focus on the input or the button, it's and if we blur or we go away from the input, we're going to set the focus as false. And then on the next tick, the next rendering cycle, we're going to go ahead and call that handler. And if both fire inputs or excuse me, if our input and our button have both lost focus, then we'll just trigger that dispatch, that event. We do need to adjust our fit or form view though. As a reminder, this is what it looked like. Very simple. It's about to get more complicated. We're gonna to override the get method and we're gonna to check if the request has is an hdmx request using Adam Johnson's Jango 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 PaaS 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 form data form clean data, and then we're going to render the form using a custom template with the paral, using the fragment, the hash, the number sign. And we're going to target that template partial that we defined earlier, the fill partial. I know that's a lot to follow, especially in talk slibut. Basically, it just means that if I focus on this, it's fine, but if I go away, field 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, kind of just go around, pop around it and just kind of just works until the hairy to get there, but it just kind of works and still know lines of JavaScript that we have written personally. Okay. So that's that was kind of the hard part and now we're going to kind of spice it up a little bit or put some sugar on top. Let's check if the current password that is given to us matches what the user's password is. Since this is a talk demo, I'm just going of hard coat it to dcuus 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. If it doesn't match, we'll return a validation or raise a validation error. And now if I don't type, you know, you can see not ddc us 2024 does not match current password. That's just what we want. But if I type in light 24, we're all good. We're all good. Okay. I believe this is the last step. You know I'm like king, 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 gonna to put import from Jango, control off the password validation module, and then set the help text to the password validators, help text html, and then also throw a new clean method for a new password, grab it from the clean data and PaaS it through the password validation. And so now, now as great as a user, I know exactly what to expect except do so if I do, a single number comes back with my validation in line. I think that is it. Yep. So I'm going to 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 in place. And so you know exactly which ones you have yet to do. You can add a progress more to see how long it is, how close to eight characters you are, kind of the mine reels. And so that's it. That's a. I think I went a little too fast. I was excited. I'm excited. This stuff gets me excited. Yeah, that's kind of it. I can make more slides. It's like y'all want to sit there and watch that. So the talk slides will be on my personal website later today. I was busy building those forms to do that so that qr code actually doesn't even work. But itbe there. Josh Thomas, 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 mastoon, and I'm also on blue sky. I'm giving it a try. So that's it. How long it . speaker 2: took you to find the comet . speaker 1: in their life? Probably 20, 30 minutes. Like I can even remember, I started by clicking back a couple times in the GitHub and that didn't work. And I think I wrote a little small little script and using git and just find the oldest commit on here. The first commit is actually documentation. The actual code comes from the second public commit. New. Yeah all right. This one comes . speaker 2: from an online attendee says, I'm curious, Josh, in your example you were building a form from scratch. Was that for demo purposes? Or if you run into trouble integrating these techniques into your the Jango prebuilt form classes in the past. speaker 1: I mean it mainly for the top purposes, for demonstration purposes. Kind of show you beginning to end. You wouldn't do all those kind of steps. You know, I don't do all those steps, but I was trying to do my best to show the 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, every single application, but it's there and it's not too bad. It's getting better. It's getting better every release. Thank you very much. You mentioned libraries by Adam Johnson plant. So it sounds like you have at your disposal toolbox of . speaker 2: different components. speaker 1: What was the experience of integrating them as they unified pieces of software seem to think in the same way. Well, that's a good question. I mean, it just depends on what layer in the application they're kind of working with. Jango template partials and Jango cotton, for example, both use custom template loaders. So getting that integrated together, getting in the right order. Luckily, both libraries are very excellent and they have good maintainers and they have good escape patches like they offer automatic setup of the template loaders freeease of use, but also have escape haches for manually setting all that up. So it kind of just depends on you know like those are two specific template loader libraries, but if you're using like Jango htmx and Jango cotton, or Jango tempartials, or Jango widget tweaks, like those are taking care of two different things. So I I've yet to run into major issues. Just kind of depends depends. You didn't have problems with them fighting with each other at any point. So no, not really, really good experience. Yeah. I mean, it was it was a pretty painless, pretty seamless, hopefully a nice user experience as well. speaker 2: So my question comes from the use of regular 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 mop or like a different package? Or would you just use raw Jango? speaker 1: You so let me repeat the question bags just so I make I understand you're not talking about you're talking about moving away from css entirely or using . speaker 2: a I'm just talking about not using something like bootstrap or toewind and using purely css and implementing your own. speaker 1: I'm like a person to ask. I'm deep on tailwind. I love tailwind. It helps me write css these days and it's extendable as getting fast. A pattern I like with tailwind now is to you know still fall back to css defined in a css file, but have tailwind process it so you can still access your theme, your nice design package. That's kind of the utility classes are not a nice addition, but really the theme, the design patterns, especially as someone who is design challenged, like leaning on that, I tend to just stick with that and not fall back to Ross sscbut. I mean, Ross sscis great these days. And it's like I said, it's getting better and it has just came out. There are container queries. I mean, the list goes on and on. You don't even need sas or scss these days. Really. I hope to answer your question. I'm sorry, I'm all in on when. So that's the answer. That's the answer. speaker 2: Any more questions? speaker 1: Well. speaker 2: thank you, Josh. speaker 1: and big round of applause, please.
最新摘要 (详细摘要)
概览/核心摘要 (Executive Summary)
本次演讲由 Josh Thomas 带来,主题是“现代 Django 表单的独到指南”,旨在帮助开发者利用 Django 及其生态系统中的新特性和工具,构建更动态、交互性和响应式的表单,而无需过度依赖前端 JavaScript 框架。演讲强调了 Django 表单自诞生以来作为数据清理、验证和呈现层的重要性,并回顾了其在不同 Django 版本中的主要改进,特别是 Django 4.0 引入的基于模板的表单渲染和 Django 5.0 的 as_field_group。
Josh Thomas 提出了提升 Django 表单的四个核心层面:利用平台特性(如 HTML5 输入类型和 CSS has() 选择器)、使用 as_field_group 实现自定义字段模板、谨慎使用 FormRenderer 进行全局表单渲染控制,以及结合强大的第三方库(如 Django Crispy Forms, Widget Tweaks, HTMX, Alpine.js 等)。他通过一个构建密码重置表单的实践案例,详细演示了如何整合这些技术,实现包括密码显示切换、字段分组、内联验证和自定义密码规则检查等高级功能,且几乎不编写自定义 JavaScript。演讲指出,Django 表单库近年来获得了新生,提供了许多新的 API 和工具,鼓励开发者探索和适应这些新模式,以提高开发效率和用户体验。
讲者介绍与背景
- 讲者: Josh Thomas
- 职位: Westervelt 公司高级 Web 开发者。该公司是一家木材和土地公司,拥有大量林地,并以负责任的方式利用土地。
- 经验: 约四年半的专业 Web 开发经验。
- Django 应用: Westervelt 公司使用 Django 管理狩猎租赁等业务,并拥有多个开源 Django 库(主要用于内部复用)。
关键技术与工具简介
- HTMX: 一个允许通过 HTML 属性直接访问 AJAX、CSS 过渡、WebSockets 等现代浏览器功能的库,无需编写 JavaScript。
- Alpine.js: 一个轻量级的 JavaScript 框架,用于在 HTML 标记中构建类似 Vue 或 React 的交互式组件。
- Django Template Partials: 一个 Django 库,用于将模板的一部分渲染为可重用的片段,常与 HTMX 结合使用以更新页面的特定部分。
- Tailwind CSS: 一个功能类优先的 CSS 框架,用于快速构建自定义用户界面。
- Heroicons: 由 Tailwind CSS 团队制作的 SVG 图标库。
Django Forms 概述
- 定义:
- 在 HTML 和 Web 中,表单是仅有的两个能与服务器交互的元素之一(另一个是
<a>标签)。表单是唯一能接收用户输入并允许服务器响应的元素。 - 在 Django 中,表单主要作为数据清理、验证和呈现层,扮演双重角色。它们接收用户输入,进行清理和验证(确保数据安全或符合规则),然后将表单呈现给用户进行交互。
- 在 HTML 和 Web 中,表单是仅有的两个能与服务器交互的元素之一(另一个是
- 历史:
- Django Forms 自 Django 诞生之初便已存在。
- 最早的公共提交记录可追溯到 GitHub 仓库的第二个公共提交(由 Adrienne 完成),当时名为
Jango core form fields。 - 重要里程碑:
- 0.95 (2006-2007): 首次包含在 GitHub 上的可用版本中。
- Django New Forms: 演变为我们今天所知的 Django Forms。
- 1.6: 添加了 GeoDjango 表单部件。
- 1.7: 重大表单验证版本,引入了
form.add_error等。 - 1.11 (2017): 添加了基于模板的部件渲染。
- 4.0 (2020): 引入了基于模板的整体表单渲染(此前仅限于部件),并引入了
FormRenderer类。 - 4.1: 带来了基于
div的表单模板,并支持自定义表单模板名称。 - 5.0: 引入了
as_field_group。
- 贡献趋势: 讲者通过分析 Django 仓库的提交记录发现,Forms 模块的提交量一直处于中等水平,不如
contrib.admin或auth等模块活跃,但它始终存在于核心中。
Django Forms 核心组成部分
- Form (表单): 核心组件,负责整个表单的验证和所有字段的渲染。
- FormSet (表单集): 多个表单实例的集合,处理起来较为复杂(讲者表示本次演讲不深入探讨)。
- Field (字段): 单个字段层面的表单,负责单个字段的验证和渲染。
- Widget (部件): 实际渲染用户输入控件(如
<input>、<select>)到 HTML 的部分。 - BoundField (绑定字段): 字段与数据的结合,数据可以是用户输入或初始数据。
- ErrorList / ErrorDict (错误列表/字典): 处理表单中的所有错误,并将错误与相应字段关联。
- 复杂性: 讲者指出,Django Forms 的复杂性在于这些组件如何协同工作,但其目的是为了节省开发者构建 Django 应用的时间。
提升 Django Forms 的四个层面
1. 利用平台特性 (Use the Platform)
- HTML5 输入类型:
- 常见类型:
number,email,url,password。这些类型提供浏览器内置的验证和交互,无需 JavaScript。 - Django 实现: 通过
forms.widgets.NumberInput,EmailInput,URLInput,PasswordInput等直接使用。 - 不常用但有用的类型:
color,search,tel。据讲者透露,这些输入类型将在未来 Django 版本中直接内置,但当前可通过继承widgets.Input并指定input_type来使用。 - 日期输入类型 (Date Input Types): 较为复杂,因 HTML 不处理时区数据,而 Django 需要处理。社区有讨论将其内置,但目前需手动处理。
- 常见类型:
- CSS
has()选择器:- 允许根据子元素的状态或属性来样式化父元素。
- 潜力: 在 Django Forms 中,可用于根据自定义部件的内部状态来样式化其父容器,避免复制和维护自定义部件模板。
2. 使用 as_field_group (Django 5.0 引入)
- 功能: 允许为 Django 字段定义自定义模板。
- 使用方式:
- 作为实例化字段时的参数
field_template_name。 - 在请求级别渲染不同的模板。
- 在
FormRenderer级别设置全局自定义字段模板。
- 作为实例化字段时的参数
- 模板调整: 在表单模板中,需将字段渲染方式从
{{ form.field }}改为{{ form.field.as_field_group }}。
3. 理解 FormRenderer
- 功能: 负责渲染整个表单库。
- 使用方式:
- 在表单、请求或模板级别定义自定义模板名称。
- 警告: 讲者不建议全局设置自定义
FormRenderer(form_renderer属性)。这被称为“大锤”,因为它会影响所有使用该渲染器的表单。讲者以亲身经历警示,他曾为了添加一个必填项的红色星号而全局修改,结果导致该星号意外出现在 Django Admin 和 Debug Toolbar 等所有使用该渲染器的地方。使用时需负责任。
4. 结合第三方库
- 常见库:
- Django Crispy Forms: 最流行的表单渲染库。
- Django Widget Tweaks: 同样流行,用于调整部件属性。
- Django Formset: 重新思考 Django 模板库,支持分组字段和表单集。
- 基于模板的组件库:
- Carlton's Django Template Partials: 用于创建可重用的模板片段。
- Django Components 和 Slippers: 流行的模板渲染库。
- Django Cotton: 一个较新的库,通过类似 Web Component 的语法 (
<custom-element>) 重新构想 Django 模板。
实践案例:构建密码重置表单
讲者演示了如何构建一个具有现代交互功能的密码重置表单,几乎不使用自定义 JavaScript。
⚠️ 安全警告: 讲者明确指出,此演示代码存在一个“巨大的安全漏洞”,仅用于阐述 API 用法,严禁在生产环境中使用。
初始实现与增强
- 使用
PasswordInput部件: 确保密码输入被遮蔽,并使用 Tailwind CSS 美化样式。 - 字段分组 (
as_field_group): 将两个新密码字段包裹在fieldset中,使其在视觉和逻辑上形成一个组。 - 密码显示/隐藏切换: 使用 Alpine.js 和 Heroicons 图标库,通过切换输入字段的
type属性(password或text)实现交互。
内联验证 (使用 HTMX)
- 目标: 在用户离开字段时立即进行验证,无需提交整个表单。
- 技术: 结合 Django Template Partials 和 HTMX。
- 步骤:
- 定义模板片段: 将每个字段的渲染逻辑封装为可重用的模板片段 (
field_partial)。 - 添加 HTMX 属性: 在输入字段上添加 HTMX 属性,主要包括:
hx-get: 向当前 URL 发送 GET 请求。hx-vals: 传递需验证的字段名。hx-trigger: 触发事件为自定义的password_blur事件。hx-include: 包含整个输入字段的值。hx-target: 目标更新元素为该字段的模板片段。hx-ext="morphdom": 使用 morphdom 扩展处理 DOM 更新,以保持焦点状态。
- Alpine.js 焦点管理: 引入
has_focus变量,当输入框和切换按钮都失去焦点时,触发password_blur自定义事件,从而启动 HTMX 请求。 - Django 视图层处理: 重写表单视图的
get方法,检查请求是否为 HTMX 请求且包含验证参数。如果满足条件,则仅验证指定字段并返回更新后的 HTML 片段。
- 定义模板片段: 将每个字段的渲染逻辑封装为可重用的模板片段 (
密码验证
- 当前密码匹配: 通过重写表单的
clean_current_password方法,检查当前密码是否正确。 - 新密码规则验证: 导入 Django 内置的
password_validation模块,将密码规则作为help_text显示给用户,并在clean_new_password方法中调用验证器。
核心观点与结论
- Django Forms 的复兴: 近年来,Django Forms 库获得了显著的改进和新 API,使其能够构建更现代、交互式的 Web 表单。
- 利用平台优势: 充分利用 HTML5 输入类型和现代 CSS 特性(如
has()选择器),可以显著减少对自定义 JavaScript 的依赖。 - 新 API 的力量:
as_field_group和FormRenderer等新功能提供了强大的定制能力,但FormRenderer需要谨慎使用,因为它可能产生全局影响。 - 拥抱生态系统: 结合 Django 生态系统中的优秀第三方库(如 HTMX, Alpine.js, Django Template Partials 等),可以高效地实现复杂的表单交互和内联验证。
- 持续探索: 讲者强调,这些 API 和技术相对较新,开发者仍在探索最佳实践和抽象模式。鼓励大家尝试和迭代。
推荐资源
- Luke Plant's Django HTML Patterns Repository: 提供了大量在 Django 应用中使用 HTMX 的模式和示例。
- Adam Johnson's Libraries: 包括
django-htmx和django-heroicons,为在 Django 中集成 HTMX 和 Heroicons 提供了便利。
问答环节 (Q&A Session)
- Q1: 演示中从头构建表单是否仅为演示目的?
- A1 (Josh Thomas): 主要为了演示目的,以展示
as_field_group等功能的定制渲染能力。实际开发中不会对每个表单都进行如此细致的步骤。
- A1 (Josh Thomas): 主要为了演示目的,以展示
- Q2: 如何集成 Adam Johnson 和 Luke Plant 等人的库,它们是否能统一工作?
- A2 (Josh Thomas): 不同的库作用于应用的不同层,通常不会相互冲突。例如,处理模板加载器的库(如 Django Template Partials)都提供了良好的集成选项和手动配置的“逃生舱口”,集成体验通常是无缝的。
- Q3: 如果不使用 CSS 框架(如 Tailwind),如何实现自定义样式?
- A3 (Josh Thomas): 讲者个人是 Tailwind CSS 的忠实用户。他承认原生 CSS 也在不断进步(如
has()选择器、容器查询),现在不依赖预处理器也能实现很多功能。
- A3 (Josh Thomas): 讲者个人是 Tailwind CSS 的忠实用户。他承认原生 CSS 也在不断进步(如