跳转到主要内容

一个基于Torchbox风格的django-crispy-forms模板包,改编自crispy-forms-gds

项目描述

PyPI npm PyPI downloads CI

Torchbox表单

一个基于Torchbox风格的django-crispy-forms模板包,改编自crispy-forms-gds

默认情况下,使用 tbxforms 创建的表单将类似于 GOV.UK 设计系统,尽管可以自定义许多变量。

要求

  • python >=3.8.1,<4.0
  • Django >=3.2
  • django-crispy-forms >=2.1,<3.0
  • 如果使用 WagtailBaseForm,则需要 wagtail >=2.15
  • 如果自行构建 Sass,则需要 sass >=1.33.0

[!注意] govuk-frontend 不需要安装即可使用此包。

已将 govuk-frontend==5.4.1 中的所有表单相关样式复制到此项目,将“govuk-”前缀替换为“tbxforms-”,例如,.govuk-button 更改为 .tbxforms-button,以及 @mixin govuk-clearfix 更改为 @mixin tbxforms-clearfix

对于非政府项目,无需安装完整的 GOV.UK 前端包,因为我们只需要表单相关样式。

对于政府项目,这会增加包的大小,因为必须安装 tbxformsgovuk-frontend。然而,这些项目较少见,因此没有优先考虑。

安装

您必须安装 Python 包和 NPM 包。

安装 Python 包

使用 pip 安装

pip install tbxforms

django-crispy-formstbxforms 添加到您的已安装应用中

INSTALLED_APPS = [
  # ...
  'crispy_forms',  # django-crispy-forms
  'tbxforms',
]

现在添加以下设置,告诉 django-crispy-forms 使用 tbxforms

CRISPY_ALLOWED_TEMPLATE_PACKS = ["tbx"]
CRISPY_TEMPLATE_PACK = "tbx"

安装 NPM 包

使用 NPM 安装

npm install tbxforms

注意:此包使用 Element.closestNodeList.forEachArray.includes API。如果您需要支持旧版浏览器,则需要安装和配置 polyfills。

实例化您的表单

import TbxForms from 'tbxforms';

document.addEventListener('DOMContentLoaded', () => {
    for (const form of document.querySelectorAll(TbxForms.selector())) {
        new TbxForms(form);
    }
});

将样式导入到您的项目中...

...作为无任何自定义的 CSS

@use 'node_modules/tbxforms/dist/style.css';

...或作为 Sass 以自定义变量

@use 'node_modules/tbxforms/tbxforms.scss' with (
    $tbxforms-text-colour: #000,
    $tbxforms-error-colour: #f00,
);

添加按钮样式

tbxforms 为除按钮之外的所有内容提供默认的 GOV.UK 设计系统样式,因为这些样式可能已经存在于您的项目中。

您需要为以下类编写按钮样式

  1. .tbxforms-button
  2. .tbxforms-button.tbxforms-button--primary
  3. .tbxforms-button.tbxforms-button--secondary
  4. .tbxforms-button.tbxforms-button--warning

用法

tbxforms 可用于编码的 Django 表单和编辑器控制的 Wagtail 表单。

Django 表单

所有表单都必须继承 TbxFormsMixin 混合,同时指定 Django 基础表单类(例如 forms.Formforms.ModelForm

from django import forms
from tbxforms.forms import TbxFormsMixin

class ExampleForm(TbxFormsMixin, forms.Form):
    ...

class ExampleModelForm(TbxFormsMixin, forms.ModelForm):
    ...

Wagtail 表单

创建或更新一个 Wagtail 表单

Wagtail 表单必须继承 TbxFormsMixinWagtailBaseForm

from wagtail.contrib.forms.forms import BaseForm as WagtailBaseForm
from tbxforms.forms import TbxFormsMixin

class ExampleWagtailForm(TbxFormsMixin, WagtailBaseForm):
    ...

指示一个 Wagtail 页面模型使用您的表单

在您的表单定义中(例如 forms.py)

from tbxforms.forms import BaseWagtailFormBuilder as TbxFormsBaseWagtailFormBuilder
from path.to.your.forms import ExampleWagtailForm

class WagtailFormBuilder(TbxFormsBaseWagtailFormBuilder):
    def get_form_class(self):
        return type(str("WagtailForm"), (ExampleWagtailForm,), self.formfields)

并在您的表单页面模型中(例如 models.py)

from path.to.your.forms import WagtailFormBuilder

class ExampleFormPage(...):
    ...
    form_builder = WagtailFormBuilder
    ...

渲染表单

与 Django Crispy Forms 一样,您需要将表单对象传递给 {% crispy ... %} 模板标签,例如。

{% load crispy_forms_tags %}

<html>
    <body>
        {% crispy your_sexy_form %}
    </body>
</html>

FormHelper

FormHelper 允许您更改表单的渲染行为。

继承自 TbxFormsMixin 的每个表单(即 tbxforms 中的每个表单)都将具有以下默认属性 FormHelper

  • html5_required = True
  • label_size = Size.MEDIUM
  • legend_size = Size.MEDIUM
  • form_error_title = _("There is a problem with your submission")
  • 以及来自 django-crispy-forms 的默认属性

这些可以在实例化期间更改,或 即时更改 - 下面的示例。

添加提交按钮

提交按钮不会自动添加到表单中。要添加一个,您可以扩展 form.helper.layout(以下示例)。

在实例化期间扩展

from django import forms
from tbxforms.forms import TbxFormsMixin
from tbxforms.layout import Button

class YourSexyForm(TbxFormsMixin, forms.Form):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper.layout.extend([
            Button.primary(
                name="submit",
                type="submit",
                value="Submit",
            )
        ])

或之后扩展

from tbxforms.layout import Button

form = YourSexyForm()
form.helper.layout.extend([
    Button.primary(
        name="submit",
        type="submit",
        value="Submit",
    )
])

条件性必填字段

tbxforms可以根据给定值显示/隐藏layout的部分。例如,当用户选择订阅通讯时(以下为示例),您可以只显示(并要求)电子邮件地址字段。

您可以将此逻辑应用于fielddivfieldset元素。

注意:在conditional_fields_to_show_as_required()方法中包含的任何字段名称将作为必填项显示在前端,尽管技术上required=False

字段示例

from django import forms
from django.core.exceptions import ValidationError
from tbxforms.choices import Choice
from tbxforms.forms import TbxFormsMixin
from tbxforms.layout import Field, Layout

class ExampleForm(TbxFormsMixin, forms.Form):
    NEWSLETTER_CHOICES = (
        Choice("yes", "Yes please", hint="Receive occasional email newsletters."),
        Choice("no", "No thanks"),
    )

    newsletter_signup = forms.ChoiceField(
        choices=NEWSLETTER_CHOICES
    )

    email = forms.EmailField(
        widget=forms.EmailInput(required=False)
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper.layout = Layout(
            # Add our newsletter sign-up field.
            Field.text("newsletter_signup"),

            # Add our email field and define the conditional logic.
            Field.text(
                "email",
                data_conditional={
                    "field_name": "newsletter_signup", # Field to inspect.
                    "values": ["yes"], # Value(s) to cause this field to show.
                },
            ),
        )

    @staticmethod
    def conditional_fields_to_show_as_required() -> [str]:
        # Non-required fields that should show as required to the user.
        return [
            "email",
        ]

    def clean(self):
        cleaned_data = super().clean()
        newsletter_signup = cleaned_data.get("newsletter_signup")
        email = cleaned_data.get("email")

        # Fields included within `conditional_fields_to_show_as_required()` will
        # be shown as required but not enforced - i.e. they will not have the
        # HTML5 `required` attribute set.
        # Thus we need to write our own check to enforce the value exists.
        if newsletter_signup == "yes" and not email:
            raise ValidationError(
                {
                    "email": "This field is required.",
                }
            )
        # The tbxforms JS will attempt to clear any redundant data upon submission,
        # though it is recommended to also handle this in your clean() method.
        elif newsletter_signup == "no" and email:
            del cleaned_data['email']

        return cleaned_data

容器示例

当您想一起显示/隐藏多个字段/元素时,您可以在divfieldset元素上使用与上面相同的data_conditional定义,例如:

from tbxforms.layout import HTML, Div, Field, Layout

Layout(
    Div(
        HTML("<p>Some relevant text.</p>"),
        Field.text("some_other_field"),
        Field.text("email"),
        data_conditional={
            "field_name": "newsletter_signup",
            "values": ["yes"],
        },
    ),
)

自定义行为

突出显示必填项而不是可选项

如果TBXFORMS_HIGHLIGHT_REQUIRED_FIELDS=False(或未设置),则可选字段将附加“(可选)”到其标签。这是默认行为,并被GDS推荐。

如果TBXFORMS_HIGHLIGHT_REQUIRED_FIELDS=True,则必填字段将附加星号到其标签,而可选字段将不会突出显示。

您还可以通过针对这些CSS类来设置这些标记的样式

  • .tbxforms-field_marker--required
  • .tbxforms-field_marker--optional

更改默认标签和图例类

label_sizelegend_size的可能值

  1. (默认值)
  2. 超大

进一步阅读

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面