2023-06-08 | DjangoCon Europe | Good Form: Django 4.x Form Rendering Improvements

Django 4.x表单渲染革新:内置功能强化与第三方库的未来

媒体详情

上传日期
2025-06-26 23:30
来源
https://www.youtube.com/watch?v=tEDMHWDMMUI
处理状态
已完成
转录状态
已完成
Latest LLM Model
gemini-2.5-pro

转录

下载为TXT
speaker 1: David.
speaker 2: Hi, thank you very much for all being here. So a bit of introduction. So Yeah, I'm David vis Smith. I'm a maintainer of Jango crispy forms. And I've been contributing to Jango itself since 2020. And through that work, I've become a member of Jango's triology and review team. This is the team that helps progress prs to the Jango itself. And just during this time, it's just wonderful. The community really supportive. I just think it's amazing that so many people can gather around at all and organize events like this. It's really great. I' M Smith dc one at GitHub and dasmith at Foston dot org dot.
speaker 3: So today.
speaker 2: what are we going to cover? We'll start about talking about the anatomy of form, the different parts that make up a Jango form, and the different words that Jango uses to describe with those elements. We'll then take an example form and see how we can ease the form rendering of that using the new features that we added during the Jango four series. And one of the questions that we've been asked off the back of this is, so with all these new features, do I still need my third party library? Do I still need to use crispy forms? Umso, we'll have a look at that. And then towards the end, we'll have a look towards the future and see what features might be added in the future and what else we could think about. So let's go. So we'll start off with a small sample form. So maybe it's a contact form where we want to collect the first name and last name, and maybe the day that you want to book an appointment for. So we create our form by subclassing jangoforms form. And there we can define our fields. So we've got a first name and last name field, which both character field and our day field, which is a choice field there. And if we just call string on that form, itbe rendered in a template, and we'll get the whole form, including all the fields and the inputs and all the widgets. Diving down to the next layer down, we have fields. So a form can have many fields. And just looking at that day field here, we have a choice field, which has choices. And we've also defined a custom widget here. So we want it to be a checkbox field so people can select different days that they may want to book an appointment for. So when we render that in our template, that field becomes the label, the label suffix, which is, by default, the colon and the widgets of those input elements. And one thing to be aware of is that the field that we define on our form becomes a bound field when we render it in our template. This is something that took me a little while to realize, and when the penny dropped, it really opened things up for me. So Jango's definition of a bfield is a field plus data. So a good example of this is maybe a user submits their form and it's invalid. So we want to rent that form again with the errors in those previously submitted values. We don't know those when we're defining the field on air form. We only know about those at render time. And then finally, we have our widget. So a widget is Jango's representation of a html input element. Each field has a default widget, which you can customize per field. So here we have customized the widget for the choice field to be that checkbox input that we saw. So the widget is just those input elements. And for choice widgets, you have a concept of suwidgets to give you each option within that overall widget. So our first name and last name, the widget is just the input box. But for this choice widget, it's the collection of the parts. So by default, when we call string on our form, we'll have something that looks like this. And this is a form that's rendered in table tags. That's the default that Jungo currently ships with. That may not be what your project needs. So historically, jangos shipped another couple of options. So as you will, and as p for rendering the form as an structed unaudered list or as paragraph tags, probably the as p is probably the most popular of these. And the accessibility team reviewed the styles that Jango ships with a little while ago, mainly Tiber, who's running the workshop next door. And the view was from an accessibility perspective, none of these styles are really that suitable and accessible for screen reader users. And the recommendation from the accessibility team was to introduce a new template that renders the forms in div tags. So in Jango 41, we introduced as div template with the associated methods. So from Jango 41, you can call as div. And that's got the improved accessibility features. So so it renders, it implements field sets and legends to relto group related inputs. So here we've got those checboxes grouped, so they're easier for screen reader users to navigate. At this point, there was some discussion about older methods. Should we keep those around? Should we remove them because they're not really recommended? And after a bit of discussion, we came to the conclusion that while they're not recommended, we won't remove them from the core Jango library. But in the documentation, we will recommend the as div. So all the docs in Jango will now use as div for examples, and it will recommend those over the traditional styles. So Jango .
speaker 3: wants to ship .
speaker 2: sensible defaults so as Dev will become the default in Jango five zero. And we've deprecated the old style. So to ease transition, there's a transitional renderer that got introduced in Django four. One. And you can implement this by in your settings profile setting the form renderer searching to that transitional or renderer. And if you need to retain the previous behaviour of it being rendered as a table, you can create a custom form render setting the form template name and the form template name settings to those template names directly. So by default, gigoogle ship as div template. And maybe you can style those directly and just use the out the box template. So yesterday, carton talked about Neapolitan. So I think that project is using the adiv templates that are straight out the box and just styling those using the tailwind d classes. But maybe you need a bit more customizability. Maybe for example, the requirement is to have a bit of margin between each of those fields. And Jango gives us lots of flexibility to unpack each of the elements that make up our forms. So here we've got an example where we do exactly this. We've got a bit of margin between each of our elements, and we can access each bound field on our form through a dotted name of tribute. So form last name gives us the bound field for the last name field on that form. And that bfield knows everything that you need to know about rendering a field in Jango. So we're able to call the label tag to give us the label errors for errors. And if we call string directly on the field itself, that actually gives us the field widget. So the input element there. So we can do that for the last name and the first name, but for that day, we need to do something a little bit different because we need to implement field set and legend tags so we can write those out ourselves in our template. And maybe we want to add a bit of help text as well at this point to give a prompt to the users of what they should be doing. So this is great. We can really customize the form layout in our project, but it's quite for both. I think folk would just want to call form to render their form. So let's see how we can improve this. So the first thing I would recommend folk do is use all the field arguments so you can set help text and labels and label suffixes on your field in your form definition. So here we can take that custom logic that's currently sitting in our template and define it on the field instead. And then in our template, we can access that through accessing the help text attribute on that bfield instead. So we're starting to generalize the template logic. So this has been around for some time. This is not new capability, but what is new. In Django 411, we introduce the legend tag. So traditionally, there's a label tag which will render your label in label tags. But for field sets, we need to do the same thing but in legend tags. So this got added in 41. Those that are kind of eago items amancy ago. That's great, but what about that field set? What can we do .
speaker 3: about that? So we .
speaker 2: introduced a use filter method in Jango 41, and that's available on the bfield. And what this does, it looks at the widget that's defined on that field and works out if that's a widget that needs to implement field and legend to relate those inputs together. So if you need to use the field set, then render it with a field set and legend tag. Otherwise, we can just use the the traditional label tag. All of the default widgets that come with Django itself have this setting set where required. But maybe if you've got a custom widget, you may need to just review that attribute on your widget to make sure people can make use of that in their forms. So now we're just starting to get somewhere where we've generalized the template logic. And rather than writing out each field individually and each element on each field, we can instead loop over each field in our form and render each field in exactly the same way. So suddenly we can shrink the amount of logic that we've got in our templates to render our form. And one tip here is that Jango will loop over those fields in your form in the same order that they're defined on your form or your model. But maybe in your html, you want them ordered in a different way, so you can use the field order attribute on your form and be able to customize the order that those are rendered in, so you can still make use of looping over the field. So this is great. We've now got a shorter form, shorter form logic, but we've still got to write that everywhere. And maybe we've got the same form in multiple pages in our project. We just want to get back to writing form. So what can we do? We can extract that form logic into its own template. And Jungo 400 switch form rendering to use the template engine. So all we need to do is define the template name on our form. And now when we call string on our form, it use that template name to render that form. Also in Jango 400 zero, you're able to customize that template. And per instance, so maybe you have a view and depenon some logic that's in your view, you may want to render that form with a different template. So you're able to call the random method directly on the form and provide it with a template name directly. So at this point, you're able to define a template for your form and use that per instance or perform. But maybe you've got a form template now that you can use for every form in your project. You would still have to define that template name on every form in your project. So in Jango 41, we added the ability to set a site wide form template. And the way this works is you create a custom form renderer, and you can define the form template name, site ewide on this form render. And it also works for form sets as well. So here we create a custom form renderer by soclassing the template setting and set the location of those templates on those settings. And in our settings, dot pifile give it the location of that custom form renderer. So now we can set a site wide red template on the form render in our settings. We can override that on a per form basis by defining the template name on the form, or we can define it per instance in our view. So just to recap the first .
speaker 3: part of the talk.
speaker 2: step one, move as much logic as you can out the template onto your form. Step two, use new features to help eawriting of the template logic. So the legend tag and the use field sets loop out those fields to reduce repetition, and then use those templates throughout your project. One final thing is that the switch to template rendering goes even deeper. There's an ability to customize the template for the labels using the template name attribute on your form, and also ise how errors are rendered by defining a custom error class and using that on your form as well. So now I can set templates on my form. Do I still need crispy forms? Do I still need my third party package? I think .
speaker 3: it depends.
speaker 2: I'll talk about crispy forms because I know a bit more about crispy forms. So crispy forms comes with some additional features that may be useful. So you can define your form layout in Python code. It's a little bit crazy, but what folks seem to like it. So rather than writing out all your template logic in html here, we're able to have to input side by side and maybe name an email in columns and they're on the same row. And we can write that in Python code. And crispy forms will render that. Using the templates that are assigned to each of those classes. You can do dynamic form layouts. So you can pick part of a form and you can update the attributes on part of that form. You could select all of the widgets of a certain type. So here we can select all of the password widgets on our form and just add certain classes on that form.
speaker 3: And finally.
speaker 2: the template packs. These are really .
speaker 3: powerful.
speaker 2: So if the bootstrap template packs, they come with lots of additional custom layer top Jack. So you can define moals, bootstrap floating fields, bootstrap alerts, accordions, and so on. And you can build out your form using these bootstrap components in the using that layout structure that we saw above. So you can get quite a lot of template logic a lot easier using these components. One other feature that I find quite useful, and this is shared both by Django widget tweaks and crispy forms, is the ability to add css classes to your widget in template logic. So of course, you can define the classes for your widget. So by defining a atas dict on your widget, that's on your field, that's on your form, but maybe you don't know what class you need to add at that point. So in this example, at the bottom we might have a bootstrap form, and we need to add the is invalid class. So we get the red box around the input field and highlights the the error text in red as well. We only know that once the validation logic has been running out form, so once that field is bound, so here we can say if the fields has an error, then add the class is required to style that field as an error. Otherwise we can just render the default styles. And this is something that's available in these third party packages. But I'm not aware of A A way in Jungo itself to be able to add css classes on the fly. Maybe you could do it in your view. Again, carton was talking yesterday about having kind of similar concepts grouped together. I'm not sure that adding css classes in views is a good answer. I'd rather see css classes in the template because I see css classes and html kind of being the same thing. And finally, some of the template logic can get quite complex. You might be able to read that, but that's kind of deliberate. This is just the field template from the bootstrap template pack. And by the way, this doesn't cover chkboxes or radios or file field. These are all there at their own templates as well. Some of the bootstrap logic can get quite complex because depending on what type you have, it's not just the classes that change, it's also the structure of the html itself as well. So what about the .
speaker 3: future? So far, we've .
speaker 2: talked about treating each field in the form exactly the same, looping over them one by one. But maybe someone pretty comes along and goes, that's great. I really love this form. But could you just put the first name and last name next to each other rather than .
speaker 3: underneath each other? Suddenly all that hard work will be undone.
speaker 2: You'll have to maybe write something like this, which unpacks each of those elements so you can put maybe the email and the password fielinside two dibs for the column and those nested within a row. This is much more difficult .
speaker 3: to maintain.
speaker 2: Maybe what we would like is make that bound field renderable. So rather than the form able to render the field. So here we have an as field group method on the bound field, and that renders the bounfield with its label, its errors, its help text and so on. And this is, I believe, much more manageable of laying out the fields on your form. Again, I'm going to refer to Carthon's talk again yesterday. He was talking about using htmx. And maybe we just want to render a field on its own rather than the whole form. This would be a template just for a field. So we've added this feature in Django five. Zero. So yoube able to define this in a similar sort of way as the other template. So field template name on your form render for site wide, and using the template name argument when you define your field on your form per field templates as well. Anything else, this one's a little bit bit out there. But something that we discussed is calling string on field actually gives you the widget rather than the field. But this has literally been like it forever. So it would be quite a breaking change, and we probably need a really strong consensus among the community to change this at this point in time. Thank you.
speaker 1: Thank you, David. Great. So I think we have time for a few questions. Anybody has got a question? There's two microphones to the left and to the right or the other way around, and there's one walking around.
speaker 4: Thanks for the talk. I'm wondering if you change something project wide, what will happen with the admin interface? Are we overriding everything there or are there still? Will we get bootstrap template in the admin all of a sudden .
speaker 2: or how does this work? Yeah, good question. So the admin templates are quite bespoke, Ken custom to use the admin, I guess, classes in the admin site I've looked at. Can we use some of these features in the admin? I'm not quite sure it's so simple given the complexity of the admin templates.
speaker 1: There's one more question here.
speaker 5: Thank you. I'm just wondering from a talk I saw the other day about excessive use of area labels and jangle forms by default.
speaker 2: Is that getting better now? Yeah, good question. I can't quite remember where we we got to, but there's definitely a couple of tickets open to add those for things like errors and help text. And we've made some improvements to the use of the four labels to link the inputs .
speaker 5: and the labels together. Yeah, they're available, but they're not overused by default. That was the .
speaker 2: concern I think expressed.
speaker 5: Yeah that's have a charokay. I'll look into it. So thank you.
speaker 1: Okay. Thank you, everybody. Thank you, David. Especially great talk.

最新摘要 (详细摘要)

生成于 2025-06-26 23:37

核心摘要

本次演讲由 Django Crispy Forms 维护者及 Django 核心贡献者 David Smith 主讲,深入剖析了 Django 4.x 系列在表单渲染方面的重大改进。演讲核心围绕从传统的字符串拼接渲染方式,向基于模板引擎、更灵活、更具可访问性的现代化方案演进。关键改进包括:在 Django 4.1 中引入以 <div> 为基础、符合可访问性标准的 as_div 渲染方法,并计划在 Django 5.0 中将其设为默认;通过 FormRenderer 实现全站、按表单、按实例三个层级的模板定制;以及新增 legend_taguse_fieldset 等模板辅助工具简化复杂表单的编写。演讲还对比了原生功能与第三方库(如 Crispy Forms)的优劣,指出原生功能已能满足多数需求,但 Crispy Forms 在 Python 动态布局和丰富组件包方面仍具优势。最后,演讲介绍了 Django 5.0 中新增的 as_field_group 方法,它将进一步提升单个字段布局的灵活性。

Django 表单渲染演进:从传统到现代

传统渲染方式及其局限

历史上,Django 表单主要通过 as_table(默认)、as_pas_ul 方法进行渲染。然而,经可访问性团队评估,这些基于表格或列表的布局对屏幕阅读器用户不够友好。

现代化变革:as_div 与可访问性提升

为解决此问题,Django 4.1 引入了 as_div 渲染方法。此方法利用 <div><fieldset><legend> 等 HTML 标签,为相关联的表单控件(如复选框组)提供正确的语义分组,显著提升了可访问性。

特性对比 as_table (旧默认) as_div (新默认)
HTML 结构 <table>, <tr>, <th>, <td> <div>, <fieldset>, <legend>
可访问性 语义分组能力弱,对屏幕阅读器不友好 语义清晰,通过 fieldset 提升可访问性
默认状态 Django 4.x 及更早版本的默认值 Django 5.0 起成为默认值
推荐度 不再推荐 官方推荐

Django 4.x/5.0 渲染新特性时间轴

Django 版本 关键特性 描述
4.0 模板引擎渲染 表单渲染切换为使用 Django 模板引擎,允许通过 template_name 属性在表单类或实例上指定自定义模板。
4.1 全站模板与 as_div 引入 FormRenderer,支持在 settings.py 中配置全站默认表单模板。正式引入 as_div 方法,并添加 legend_taguse_fieldset 辅助工具。
5.0 字段级渲染与默认值变更 as_div 成为默认渲染方式。引入 as_field_group 方法,允许对单个字段(包含标签、错误、帮助文本)进行独立的、可模板化的渲染。

简化与通用化模板的最佳实践

  1. 逻辑移至表单定义:将 labelhelp_text 等元数据直接在表单字段中定义,而非在模板中硬编码,使模板更具通用性。
  2. 利用新模板工具
    • use_fieldset:在模板中判断字段是否需要 <fieldset> 包裹。
    • legend_tag:为 fieldset 渲染 <legend> 标签。
  3. 循环渲染字段:通过在模板中循环遍历表单字段 (for field in form),并应用统一的渲染逻辑,可大幅减少代码重复。可使用 field_order 属性控制循环顺序。
  4. 分层级模板定制
    • 全站级:通过自定义 FormRenderer 并配置 FORM_RENDERER 设置。
    • 表单级:在表单类上定义 template_name 属性。
    • 实例级:在视图中调用 form.render('template.html') 方法。

原生功能 vs. 第三方库 (Crispy Forms) 对比

功能维度 Django 原生功能 Crispy Forms
布局定义 在 HTML 模板中定义布局。 在 Python 代码中使用 LayoutHelper 对象定义布局,更接近后端逻辑。
动态 CSS 类 难以在模板中根据运行时状态(如验证失败)动态添加 CSS 类。 支持在模板中通过 filter 轻松添加 is-invalid 等动态类。
组件生态 提供基础表单元素。 提供丰富的模板包(如 Bootstrap),内置模态框、手风琴等复杂组件,开箱即用。
灵活性 Django 5.0 的 as_field_group 提升了字段级布局的灵活性。 布局对象提供了极高的灵活性,可轻松实现多列、并排等复杂布局。
依赖 无额外依赖。 需要添加第三方库。

结论:Django 原生功能已足够强大,能满足大部分表单渲染需求。但对于需要复杂动态布局或深度集成前端框架(如 Bootstrap)的项目,Crispy Forms 仍是极具价值的选择。

未来展望与讨论

  • 已实现:字段级渲染 (as_field_group):Django 5.0 引入了在绑定字段上直接调用的 as_field_group 方法。这使得将“姓”和“名”等字段并排显示的复杂布局变得简单可控,开发者可以为单个字段组定义独立的模板。
  • 待讨论:str(field) 行为变更:一个被提出的想法是让 str(field) 直接渲染完整的字段组(标签、小部件、错误等),而非仅仅是小部件。由于这将是重大破坏性变更,需要广泛的社区共识才能推进。

问答环节精要

  • Admin 界面兼容性:演讲者指出,Django Admin 的模板高度定制化和复杂,直接应用这些新的表单渲染特性存在困难。
  • ARIA 标签使用:针对 ARIA 标签(特别是 aria-label)的使用,演讲者确认已有相关工单在跟进,旨在为错误信息和帮助文本添加适当的 ARIA 属性,并已对输入与标签的 for 属性关联做出改进。关于是否“过度使用”,演讲者未给出明确结论。