Django缺失的小部件和表单操作库
项目描述
django-formset – Django表单更好的用户体验
这个库以比Django内部对表单集提供的实现更好的用户体验来处理单个表单和表单集合。
让我们用一个简短的例子来解释。假设我们有一个包含三个字段的Django表单
from django.forms import fields, forms
class AddressForm(forms.Form):
recipient = fields.CharField(label="Recipient")
postal_code = fields.CharField(label="Postal Code")
city = fields.CharField(label="City")
创建一个Django FormView后,我们可以使用略微修改的模板渲染上述表单
{% load formsetify %}
{% render_form form "bootstrap" %}
这将使用Bootstrap的样式指南建议的布局和CSS类来渲染我们的表单
或者如果使用替代CSS类渲染
{% load formsetify %}
{% render_form form "bootstrap" field_css_classes="row mb-3" label_css_classes="col-sm-3" control_css_classes="col-sm-9" %}
或者如果使用Tailwind渲染器渲染
{% load formsetify %}
{% render_form form "tailwind" %}
django-formset为所有主要CSS框架提供表单渲染器,例如Bootstrap 5、Bulma、Foundation 6、Tailwind和UIkit。
多个输入小部件
此外,它可以渲染Django提供的所有小部件。这包括多个复选框和单选选择项,甚至包含多个选项组
文件上传小部件
文件上传是异步进行的,将有效负载上传与其表单提交分离。它提供了一个拖放组件和一个文件选择按钮。这允许在表单提交前预览上传的文件。这也使得提交速度更快,因为文件已经在服务器上的临时位置。
空文件上传 | 待上传文件 |
---|---|
针对 <select>
和 <select multiple>
组件的替代方案
默认的HTML <select>
组件可以用具有自动完成的对应组件替换。不需要额外的端点,因为这已经由控制表单的Django视图处理。
默认的HTML <select multiple="multiple">
组件可以用两个不同的组件替换,一个将选定的选项以内联形式保留,另一个将它们保留在“选择从”和“选定的选项”字段内。
具有自动完成的复选框多选 | 具有来源和目标的复选框多选 |
---|---|
类似的组件可以在Django管理界面中找到,以使多对多关系可编辑。在 django-formset 中,具有来源和目标的正确组件提供了一些额外的功能
- 它可以处理来源包含太多条目而无法一次性加载的关系。相反,此组件在搜索选项时查询数据库。它使用相同的自动完成端点。
- 组件的右侧部分也可以进行过滤。
- 组件具有重做/撤销功能,以防用户错误地选择了错误的选项。
- 可选地,组件右侧的选定选项可以进行排序。此顺序随后反映在多对多关系的额外字段上。
按钮操作
在 django-formset 中,用于提交的按钮可以包含一个 动作链。例如,这允许在提交数据时禁用按钮,并/或添加一个旋转图标。也可以指定成功页面为HTML链接,而不是将其硬编码在Django视图中。设计提交按钮时,有一套完整的预定义动作可供选择。
即时表单验证
每个字段在失去焦点时立即进行验证。这提供了即时反馈,并指示在提交表单时哪些用户输入将不被接受。浏览器端验证约束与为每个Django字段在Python中定义的约束完全相同。
并非每个值或其组合都可以通过浏览器进行验证,而可能被后端应用程序拒绝。例如,clean()
和/或 clean_FIELDNAME()
方法可能会使用某种内部逻辑对值提出抱怨。
这些服务器端错误被发送回客户端,并在附近显示在拒绝的字段附近,而无需重新渲染整个页面。在成功的情况下,将加载给定的页面(或执行其他替代操作)。
分组表单
正如“formset”的名字所暗示的,django-formset 允许管理多个表单。因此,可以创建表单集合,甚至将这些集合嵌套在一起。可以声明集合具有兄弟姐妹,允许它们被实例化多次。这类似于Django的Stacked-和Tabular-Inlines,但允许无限级的嵌套。此外,这样的具有兄弟姐妹的集合可以可选地进行排序。
表单集合对于创建具有一对一关系的模型的编辑器也很有用。例如,Django管理界面需要使用Stacked-或Tabular-Inline,而后者设计用于处理一对多关系。使用集合,这两个相互关联的模型可以用看似相同的表单(尽管在幕后这些是分离的实体)进行处理。
条件隐藏/禁用
由于每个表单集都保存其状态(字段当前值),这些信息可用于有条件地隐藏或禁用其他字段,甚至整个字段集。
通过在输入字段或字段集上添加特殊属性 df-show="condition"
、df-hide="condition"
或 df-disable="condition"
,可以隐藏或禁用这些标记字段。这个 condition
可以是任何表达式,用于评估表单集的当前字段值。
替代小部件
django-formset 为 Django 提供了许多字段的替代小部件。例如
Django 的 DateField
小部件
现代浏览器提供了内置的日期选择器,现在可以用来代替默认的 <input type="text">
小部件。除此之外,django-formset 提供了自定义的 Web 组件,这些组件会根据所选的 CSS 框架进行适配。这使得日期选择器可以以与表单其余部分相同的样式渲染。
这一切是如何工作的?
django-formset 采用了 Django 4 中引入的 表单渲染器。这允许为每个支持的 CSS 框架创建特殊的渲染器。除了框架供应商提出的表单结构之外,这个库还为每个字段添加了包含约束信息的私有 HTML 标签,这些约束信息是在 Python 中声明的。
然后,表单或表单集合被包装在提供的 Web 组件 <django-formset>
中。构成该 Web 组件的 JavaScript 部分(实际上是 TypeScript)负责处理表单验证、提交、实例化或移除集合同级元素等。
上述一些小部件(具有自动完成的下拉列表、文件上传)也需要 JavaScript 代码。这些小部件的客户端功能也由该 Web 组件处理。需要自动完成的小部件使用与该 Web 组件本身相同的端点。因此,无需在 URL 路由器中添加额外的端点。
这意味着,最终用户必须 仅 导入此单个 JavaScript 文件,并将单个表单或表单集合包装到单个 HTML 元素中,例如
<django-formset endpoint="/path/to/myproject/view" csrf-token="…">
…
</django-formset>
处理表单或表单集合的 Django 视图需要一个特殊的混合类,但除此之外,它与 Django 提出的相同,例如其 FormView。
表单类可以未作更改地重用,除非需要或希望替换小部件(例如,FileField
需要不同的小部件)。
参考文档
带有交互式示例的参考文档可以在 https://django-formset.fly.dev/ 找到。
动机
我们不是使用 <form>
标签并包含所有字段,而是将整个表单包装在特殊的 Web 组件 <django-formset>
中。这允许客户端使用 fetch API 与 Django 视图(我们称之为“端点”)通信。这意味着可以将多个 <form>
元素包装到表单集中。这也意味着提交 <button>
可以放置在 <form>
元素之外。通过这样做,表单的有效负载使用 Content-Type: application/json
而不是通常的 Content-Type: application/x-www-form-urlencoded
发送。使用 JSON 传输有效负载,表单数据被映射到 JavaScript 对象,而表单集合由嵌套的数据结构表示。
设计这个库的主要目标是使编程接口尽可能接近 Django 处理表单、模型和视图的方式。
摘要
-
在提交之前,所有表单字段都由浏览器进行预验证,使用与在 Python 中为每个 Django 表单或模型字段声明的相同约束。
-
表单的数据通过 Ajax 请求发送,防止整个页面重新加载。这提供了更好的用户体验。
-
服务器端验证错误被发送回浏览器,并在被拒绝的表单字段附近渲染。
-
非字段验证错误与表单一起渲染。
-
CSRF令牌通过HTTP头进行处理,因此无需在每个表单中添加隐藏的输入字段。
-
可以使用特定CSS框架的风格指南来渲染表单。目前 django-formset 包含以下渲染器:
- Bootstrap 5,
- Bulma,
- Foundation 6,
- Tailwind [^1]
- UIKit
创建渲染器通常需要约50行代码,而大多数小部件甚至可以使用Django提供的默认模板进行渲染。
-
不需要外部JavaScript依赖。客户端部分使用纯TypeScript编写,并编译为单个可移植的JS文件。
-
支持Django目前提供的所有标准小部件(除GeoSpacials外)。
-
文件上传异步处理,将有效载荷上传与表单提交分离。
-
具有过多条目的选择框可以通过服务器使用搜索查询进行筛选。
-
只有少数字段的单选按钮和复选框可以内联渲染,而不是相互之下。
-
提交按钮可以配置为一系列动作。
-
表单集可以将多个表单组合成集合。集合可以嵌套。提交时,此表单或表单集合的数据作为单独的实体发送到服务器。
-
此类表单集合可以声明具有列表兄弟,可以通过一个“添加”按钮和多个“删除”按钮来更改长度。
-
可以使用布尔表达式作为条件隐藏或禁用表单字段或字段集。
^[1]:Tailwind在这里是特殊的,因为它不包含开箱即用的表单控件类。相反,django-formset 提供了一套适合Tailwind的CSS类。