Django如何覆盖表单HTML标签模板?

18

在我的settings.py中,我已经设置了

FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
现在可以添加自己的模板了:

now in can add my own templates in:

->

现在可以加入自己的模板:

<project>/templates/django/forms/widgets/
或者
<app>/templates/django/forms/widgets/

这很好用!然而,我找不到在哪里覆盖默认的html(form)标签。

class TestForm(forms.Form):
    first_name = forms.CharField(label="First name", max_length=50)
    last_name = forms.CharField(label="Last name")
    nick_name = forms.CharField(required=False)

上面的表格将会渲染标签如下:


<label for="id_first_name">First name:</label>

我想以不同的方式呈现标签。因此,我认为只需添加一个html标签模板即可:templates/django/forms/widgets/label.html

这种方法无效。阅读了Django文档,但我找不到如何对标签进行更改的方法。显然标签不是小部件。

https://docs.djangoproject.com/en/1.11/ref/forms/widgets/#built-in-widgets

我的问题是,我应该在哪里/如何更改默认标签?


当你只想要样式化标签时,你可以进入CSS并编写label{width:200px;...}。当你想要正确地样式化表单时,我建议使用Widget-Tweaks。这是Django表单最个性化的样式选项,因为你可以在每个表单属性上使用纯CSS。请参见我的答案此处 - hansTheFranz
1
@hansTheFranz 谢谢。但我不是在寻求样式,我想重写标签。例如,不是<label>而是<p>标签。 - Roger
1
你说得对,标签不是一个小部件。 <label> 标签是在 boundfield 对象的一个方法中创建的。覆盖标签并不是那么简单的。我也遇到了同样的问题。https://github.com/django/django/blob/master/django/forms/boundfield.py#L164 - Mike Monteith
3个回答

3

以下是一个测试通过的解决方案(在 Django 3.1 上测试通过):

创建一个 Django 表单(Form)的子类,并将 LABEL_TEMPLATE 设置为您想要的模板。

myapp/base_form.py

from django import forms
from django.forms.utils import flatatt
from django.utils.html import conditional_escape, format_html
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _

LABEL_TEMPLATE = '<p{}>{}</p>'


class CustomLabelBoundField(forms.boundfield.BoundField):


    def label_tag(self, contents=None, attrs=None, label_suffix=None):
        """
        Wrap the given contents in a <label>, if the field has an ID attribute.
        contents should be mark_safe'd to avoid HTML escaping. If contents
        aren't given, use the field's HTML-escaped label.

        If attrs are given, use them as HTML attributes on the <label> tag.

        label_suffix overrides the form's label_suffix.
        """
        contents = contents or self.label
        if label_suffix is None:
            label_suffix = (self.field.label_suffix if self.field.label_suffix is not None
                            else self.form.label_suffix)
        # Only add the suffix if the label does not end in punctuation.
        # Translators: If found as last label character, these punctuation
        # characters will prevent the default label_suffix to be appended to the label
        if label_suffix and contents and contents[-1] not in _(':?.!'):
            contents = format_html('{}{}', contents, label_suffix)
        widget = self.field.widget
        id_ = widget.attrs.get('id') or self.auto_id
        if id_:
            id_for_label = widget.id_for_label(id_)
            if id_for_label:
                attrs = {**(attrs or {}), 'for': id_for_label}
            if self.field.required and hasattr(self.form, 'required_css_class'):
                attrs = attrs or {}
                if 'class' in attrs:
                    attrs['class'] += ' ' + self.form.required_css_class
                else:
                    attrs['class'] = self.form.required_css_class
            attrs = flatatt(attrs) if attrs else ''
            contents = format_html(LABEL_TEMPLATE, attrs, contents)
        else:
            contents = conditional_escape(contents)
        return mark_safe(contents)




def get_bound_field(field, form, field_name):
    """
    Return a BoundField instance that will be used when accessing the form
    field in a template.
    """
    return CustomLabelBoundField(form, field, field_name)


class CustomLabelForm(forms.Form):

    def __getitem__(self, name):
        """Return a BoundField with the given name."""
        try:
            field = self.fields[name]
        except KeyError:
            raise KeyError(
                "Key '%s' not found in '%s'. Choices are: %s." % (
                    name,
                    self.__class__.__name__,
                    ', '.join(sorted(self.fields)),
                )
            )
        if name not in self._bound_fields_cache:
            self._bound_fields_cache[name] = get_bound_field(field, self, name)
        return self._bound_fields_cache[name]

继承自 CustomLabelForm 的表单:

myapp/forms.py

from django import forms
from myapp.base_form import CustomLabelForm

class TestForm(CustomLabelForm):
    first_name = forms.CharField(label="First name", max_length=50)
    last_name = forms.CharField(label="Last name")
    nick_name = forms.CharField(required=False)



这将产生 <p for="id_first_name">名字:</p>

3

您可以重写表单的__init__()方法:

def __init__(self, *args, **kwargs):
    self.fields['field_name'].label = 'Your new label here'

当我使用PasswordChangeForm时,它对我无效。 - zx1986

1

在Django中覆盖现有的表单小部件是可以实现的。

首先,您需要将'django.forms'添加到您的INSTALLED_APPS中,以将其视为应用程序。

INSTALLED_APPS = [
    ...
    "django.forms",
    ...
]

然后将FORM_RENDERER添加到您的settings.py文件中。
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'

关于模板设置的更多信息可以在Django文档中找到。

之后,将您想要覆盖的任何widget文件从django/forms/widgets/文件夹复制到您自己的projectname/templates/django/forms/widgets文件夹。自定义吧!

希望这有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接