将类添加到 Django label_tag() 输出

发布于 2024-07-11 10:32:18 字数 523 浏览 8 评论 0原文

我需要某种方法将类属性添加到表单字段的 label_tag() 方法的输出中。

我看到可以传入 attrs 字典,并且我已经在 shell 中测试了它,我可以执行以下操作:

for field in form:
    print field.label_tag(attrs{'class':'Foo'})

我将看到 class='Foo' 在我的输出中,但我没有看到从模板添加 attrs 参数的方法 - 事实上,模板是专门针对此而设计的,不是吗?

我的表单定义中有没有办法定义要在标签中显示的类?

在表单中,我可以执行以下操作来为输入提供一个类,

self.fields['some_field'].widget.attrs['class'] = 'Foo'

我只需要让它输出 的类。

I need some way to add a class attribute to the output of the label_tag() method for a forms field.

I see that there is the ability to pass in an attrs dictionary and I have tested it in the shell and I can do something like:

for field in form:
    print field.label_tag(attrs{'class':'Foo'})

I will see the class='Foo' in my output, but I don't see a way to add an attrs argument from the template - in fact, templates are designed specifically against that, no?

Is there a way in my form definition to define the class to be displayed in the label?

In the form, I can do the following to give the inputs a class

self.fields['some_field'].widget.attrs['class'] = 'Foo'

I just need to have it output the class for the <label /> as well.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

忆悲凉 2024-07-18 10:32:18

技术 1

我对另一个答案的断言提出异议,即过滤器将“不太优雅”。 正如您所看到的,它确实非常优雅。

@register.filter(is_safe=True)
def label_with_classes(value, arg):

    return value.label_tag(attrs={'class': arg})

在模板中使用它同样优雅:

{{ form.my_field|label_with_classes:"class1 class2"}}

技术 2

或者,我发现的更有趣的技术之一是:将 * 添加到必填字段

您为 BoundField.label_tag 创建一个装饰器,它将通过适当设置的 attrs 来调用它。 然后你猴子修补 BoundField 以便调用 BoundField.label_tag 调用装饰函数。

from django.forms.forms import BoundField

def add_control_label(f):
    def control_label_tag(self, contents=None, attrs=None):
        if attrs is None: attrs = {}
        attrs['class'] = 'control-label'
        return f(self, contents, attrs) 
    return control_label_tag

BoundField.label_tag = add_control_label(BoundField.label_tag)    

Technique 1

I take issue with another answer's assertion that a filter would be "less elegant." As you can see, it's very elegant indeed.

@register.filter(is_safe=True)
def label_with_classes(value, arg):

    return value.label_tag(attrs={'class': arg})

Using this in a template is just as elegant:

{{ form.my_field|label_with_classes:"class1 class2"}}

Technique 2

Alternatively, one of the more interesting technique I've found is: Adding * to required fields.

You create a decorator for BoundField.label_tag that will call it with attrs set appropriately. Then you monkey patch BoundField so that calling BoundField.label_tag calls the decorated function.

from django.forms.forms import BoundField

def add_control_label(f):
    def control_label_tag(self, contents=None, attrs=None):
        if attrs is None: attrs = {}
        attrs['class'] = 'control-label'
        return f(self, contents, attrs) 
    return control_label_tag

BoundField.label_tag = add_control_label(BoundField.label_tag)    
他不在意 2024-07-18 10:32:18

如何将 CSS 类添加到 forms.py 中的表单字段,例如:

class MyForm(forms.Form):
    title = forms.CharField(widget=forms.TextInput(attrs={'class': 'foo'}))

然后我只需在模板中执行以下操作:

<label for="id_{{form.title.name}}" class="bar">
    {{ form.title }}
</label>

当然,这可以轻松修改为在模板中的 for 循环标记中工作。

How about adding the CSS class to the form field in the forms.py, like:

class MyForm(forms.Form):
    title = forms.CharField(widget=forms.TextInput(attrs={'class': 'foo'}))

then I just do the following in the template:

<label for="id_{{form.title.name}}" class="bar">
    {{ form.title }}
</label>

Of course this can easily be modified to work within a for loop tag in the template.

何必那么矫情 2024-07-18 10:32:18

自定义模板标签 似乎是解决方案。 自定义过滤器也可以,尽管它可能不太优雅。 但在这两种情况下,您都需要回退到自定义表单呈现。

如果这是一项非常重要的任务; 我将创建一个 Mixin,它允许我使用标签类注释表单字段,并使用这些类提供表单渲染方法。 这样下面的代码就可以工作:

{{ form.as_table_with_label_classes }}

但我想问; 你真的需要标签标签上的类吗? 我的意思是 HTML 设计方面。 是否绝对有必要在其中添加一个类? 难道不能用像这样的CSS来解决吗:

encapsulating_selector label {
    some-attr: some-value;
}

我有时使用 jQuery 对于这种情况; 如果它有效,它会改进页面,但如果它不起作用,也不会是一场灾难。 并保持 HTML 源代码尽可能精简。

A custom template tag seems to be the solution. A custom filter would also do, although it can be less elegant. But you would need to fall back to custom form rendering in both cases.

If this is a task of high importance; I'd create a Mixin that allows me to annotate the form fields with label classes and supplies form rendering methods using those classes. So that the following code works:

{{ form.as_table_with_label_classes }}

But I'd like to ask; Do you really need a class on the label tag? I mean HTML design-wise. Is it absolutely necessary to add a class in there? Couldn't it be solved with some CSS like:

encapsulating_selector label {
    some-attr: some-value;
}

I sometimes use jQuery for such cases where; it will improve the page if it works, but it won't be a disaster if it doesn't. And keep the HTML source as lean as possible.

橘虞初梦 2024-07-18 10:32:18

我同意第一个答案,使用 css 可以做到这一点,但是。
这是 django 源代码中的原因是什么?

在 django.forms.forms.py 中,有一个定义显示有在标签中显示 attrs 的代码:

class BoundField(StrAndUnicode): 
    ...
    def label_tag(self, contents=None, attrs=None):
        contents = u'<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, unicode(contents))

但是 _html_output 调用这个函数时没有 attrs:

label = bf.label_tag(label) or ''

所以看来 django 是部分的准备这样做但实际上并没有使用它。

I am agree with answer number one, with css this could be done, but.
What is the reason for this to be in django source?

In django.forms.forms.py there's this definition that shows there's code to display attrs in labels:

class BoundField(StrAndUnicode): 
    ...
    def label_tag(self, contents=None, attrs=None):
        contents = u'<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, unicode(contents))

but _html_output calls this function without attrs:

label = bf.label_tag(label) or ''

So it seems that django is partially prepared to do this but actually it does not use it.

↙温凉少女 2024-07-18 10:32:18

有点晚了,但遇到了类似的问题。 希望这对您有帮助。

class MyForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['myfield1'].widget.attrs.update(
            {'class': 'form-control'})
        self.fields['myfield2'].widget.attrs.update(
            {'class': 'form-control'})

    def as_two_col_layout(self):

        return self._html_output(
            normal_row='<div class="form-group"><span class="col-xs-2">%(label)s</span> <div class="col-xs-10">%(field)s%(help_text)s</div></div>',
            error_row='%s',
            row_ender='</div>',
            help_text_html=' <span class="helptext">%s</span>',
            errors_on_separate_row=True)

    class Meta:

        model = mymodel
        fields = ['myfield1', 'myfield2']

A bit too late but came across a similar problem. Hope this helps you.

class MyForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['myfield1'].widget.attrs.update(
            {'class': 'form-control'})
        self.fields['myfield2'].widget.attrs.update(
            {'class': 'form-control'})

    def as_two_col_layout(self):

        return self._html_output(
            normal_row='<div class="form-group"><span class="col-xs-2">%(label)s</span> <div class="col-xs-10">%(field)s%(help_text)s</div></div>',
            error_row='%s',
            row_ender='</div>',
            help_text_html=' <span class="helptext">%s</span>',
            errors_on_separate_row=True)

    class Meta:

        model = mymodel
        fields = ['myfield1', 'myfield2']
仄言 2024-07-18 10:32:18
class CustomBoundField(BoundField):
    def label_tag(self, contents=None, attrs=None):
        if self.field.required:
            attrs = {'class': 'required'}
        return super(CustomBoundField, self).label_tag(contents, attrs)

class ImportViewerForm(forms.Form):
    url = fields.URLField(widget=forms.TextInput(attrs={'class': 'vTextField'}))
    type = fields.ChoiceField(choices=[('o', 'Organisation'), ('p', 'Program')], widget=forms.RadioSelect,
                              help_text='Url contain infornation about this type')
    source = fields.ChoiceField(choices=[('h', 'hodex'), ('s', 'studyfinder')], initial='h', widget=forms.RadioSelect)

    def __getitem__(self, name):
        "Returns a BoundField with the given name."
        try:
            field = self.fields[name]
        except KeyError:
            raise KeyError('Key %r not found in Form' % name)
        return CustomBoundField(self, field, name)

class Media:
    css = {'all': [settings.STATIC_URL + 'admin/css/forms.css']}

您需要更改 BoundField 类中的方法 label_tag ,并在表单中使用它

class CustomBoundField(BoundField):
    def label_tag(self, contents=None, attrs=None):
        if self.field.required:
            attrs = {'class': 'required'}
        return super(CustomBoundField, self).label_tag(contents, attrs)

class ImportViewerForm(forms.Form):
    url = fields.URLField(widget=forms.TextInput(attrs={'class': 'vTextField'}))
    type = fields.ChoiceField(choices=[('o', 'Organisation'), ('p', 'Program')], widget=forms.RadioSelect,
                              help_text='Url contain infornation about this type')
    source = fields.ChoiceField(choices=[('h', 'hodex'), ('s', 'studyfinder')], initial='h', widget=forms.RadioSelect)

    def __getitem__(self, name):
        "Returns a BoundField with the given name."
        try:
            field = self.fields[name]
        except KeyError:
            raise KeyError('Key %r not found in Form' % name)
        return CustomBoundField(self, field, name)

class Media:
    css = {'all': [settings.STATIC_URL + 'admin/css/forms.css']}

You need change method label_tag in BoundField class, and use it in form

葬花如无物 2024-07-18 10:32:18
@register.simple_tag
def advanced_label_tag(field):
    """ Return form field label html marked to fill by `*` """
    classes = []
    attrs = {}
    contents = force_unicode(escape(field.label))

    if field.field.required:
        classes.append(u'required')
        contents = force_unicode('%s <span>*</span>'%escape(field.label))

    if classes:
        attrs['class'] = u' '.join(classes)

    return field.label_tag(contents=contents, attrs=attrs)
@register.simple_tag
def advanced_label_tag(field):
    """ Return form field label html marked to fill by `*` """
    classes = []
    attrs = {}
    contents = force_unicode(escape(field.label))

    if field.field.required:
        classes.append(u'required')
        contents = force_unicode('%s <span>*</span>'%escape(field.label))

    if classes:
        attrs['class'] = u' '.join(classes)

    return field.label_tag(contents=contents, attrs=attrs)
溺深海 2024-07-18 10:32:18

我们还可以使用 {{field.label}} 和 {{field.id_for_label}}

在 HTML 中呈现为-

<label class="your_class_name" id="id_name">Name</label>

we can also use {{field.label}} and {{field.id_for_label}}

<label class="your_class_name" id="{{form.link.id_for_label}}">{{form.link.label}}</label>

Render in HTML as-

<label class="your_class_name" id="id_name">Name</label>
淡淡的优雅 2024-07-18 10:32:18

注意:希望即将推出的 Django 4.0 将使这变得更加容易,因为它提供了 基于模板的表单渲染

在那之前:

OP要求一种使用

user240515user2732686 的回答确实提供了一些实施建议,但没有提供任何理由。
大多数基于自定义模板标签的其他解决方案都需要我们手动渲染表单字段,因此如果我们只想使用 {{ form }},它们就不起作用。

因此,除了所有这些答案之外,这里还尝试提供更多背景信息。

关于 label_tag

表单标签由 BaseForm.as_table()as_ul()as_p() 呈现 快捷方式,通过“私有”BaseForm._html_output( ) 方法,如 来源

这是通过调用 BoundField.label_tag() 来完成的,可以看出 此处
label_tag() 方法采用 attrs 参数以及 标记的附加 HTML 属性。

但是,问题是 BaseForm._html_output() 调用 label_tag() 而没有 attrs,并且没有简单的替代方法来设置 >attrs 参数。

如何扩展label_tag?

Django 的 contrib.admin 通过扩展其 AdminField 中的 label_tag() 方法解决了这个问题,从 来源

要扩展 BoundField.label_tag(),我们可以 创建自定义BoundField

class MyBoundField(forms.BoundField):
    def __init__(self, form, field, name, label_attrs=None):
        super().__init__(form, field, name)
        self.label_attrs = label_attrs

    def label_tag(self, contents=None, attrs=None, label_suffix=None):
        if attrs is None:
            attrs = dict()
        attrs.update(self.label_attrs or {})
        return super().label_tag(contents, attrs, label_suffix)

现在我们可以创建一个具有特定标签属性的绑定字段,但是我们用它做什么呢?

如何使用自定义绑定字段?

绑定字段使用 forms.Field 实例化。 get_bound_field(),因此,我们可以重写该方法以返回自定义绑定字段:

class MyField(forms.Field):
    # note typically we would use any of django's forms.Field subclasses
    def __init__(self, *args, **kwargs):
        # we could also set label_attrs here, based on the field properties
        self.label_attrs = kwargs.pop('label_attrs', None)
        super().__init__(*args, **kwargs)

    def get_bound_field(self, form, field_name):
        return MyBoundField(
            form=form, field=self, name=field_name, label_attrs=self.label_attrs)

然后我们可以在 Form 上使用自定义字段:

class MyForm(forms.Form):
    some_field = MyField(..., label_attrs={'class': 'my-class'})

但是如果我们想对所有对象执行此操作该怎么办我们的表单字段?

如何为所有表单字段使用自定义绑定字段?

最后,在BaseForm.__getitem__()中调用了Field.get_bound_field()(参见来源)。 这意味着我们可以通过调用例如my_form['some_field']来获取绑定字段。

为了将我们的自定义绑定字段用于所有表单字段,我们可以 monkey patch field.get_bound_field 对于表单中的所有字段,或者我们可以覆盖表单的 __getitem__() 以忽略 get_bound_field() 并直接使用 MyBoundField

这是一个覆盖的示例,它主要是 原始来源,除了 MyBoundField 行:

class MyForm(forms.Form):
    label_attrs = {'class': 'my-class'}
    
    def __getitem__(self, name):
        """Return a MyBoundField with the given name."""
        try:
            field = self.fields[name]
        except KeyError:
            ...  # left out for clarity
        if name not in self._bound_fields_cache:
            self._bound_fields_cache[name] = MyBoundField(
                form=self, field=field, name=name, label_attrs=self.label_attrs)
        return self._bound_fields_cache[name]

最后,这一切对于一些样式来说似乎是一个很多麻烦。

NOTE: Hopefully the upcoming Django 4.0 will make this a whole lot easier, as it offers template based form rendering.

Until then:

The OP asks for a way to use BoundField.label_tag() in a form definition.

The answers by user240515 and user2732686 do provide some suggestions for implementation, but they do not provide any rationale.
Most other solutions based on custom template tags require us to render the form fields manually, so they do not work if we simply want to use {{ form }}.

Thus, in addition to all those answers, here's an attempt to provide some more background.

About label_tag

Form labels are rendered by the BaseForm.as_table(), as_ul(), or as_p() shortcuts, via the "private" BaseForm._html_output() method, as can be seen in the source.

This is done by calling BoundField.label_tag(), as can be seen here.
The label_tag() method takes an attrs argument with additional HTML attributes for the <label> tag.

However, the problem is that BaseForm._html_output() calls label_tag() without attrs, and there is no easy alternative for setting the attrs argument.

How to extend label_tag?

Django's contrib.admin solves this problem by extending the label_tag() method in its AdminField, as becomes clear from the source.

To extend BoundField.label_tag(), we can create a customized BoundField:

class MyBoundField(forms.BoundField):
    def __init__(self, form, field, name, label_attrs=None):
        super().__init__(form, field, name)
        self.label_attrs = label_attrs

    def label_tag(self, contents=None, attrs=None, label_suffix=None):
        if attrs is None:
            attrs = dict()
        attrs.update(self.label_attrs or {})
        return super().label_tag(contents, attrs, label_suffix)

Now we can create a bound field with specific label attributes, but what do we do with it?

How to use a custom bound field?

Bound fields are instantiated using forms.Field.get_bound_field(), so, we could override that method to return our custom bound field:

class MyField(forms.Field):
    # note typically we would use any of django's forms.Field subclasses
    def __init__(self, *args, **kwargs):
        # we could also set label_attrs here, based on the field properties
        self.label_attrs = kwargs.pop('label_attrs', None)
        super().__init__(*args, **kwargs)

    def get_bound_field(self, form, field_name):
        return MyBoundField(
            form=form, field=self, name=field_name, label_attrs=self.label_attrs)

Then we can use the custom field on our Form:

class MyForm(forms.Form):
    some_field = MyField(..., label_attrs={'class': 'my-class'})

But what if we want to do this for all our form fields?

How to use a custom bound field for all form fields?

In the end, Field.get_bound_field() is called in BaseForm.__getitem__() (see source). This means we can get a bound field by calling e.g. my_form['some_field'].

In order to use our custom bound field for all the form's fields, we could either monkey patch field.get_bound_field for all fields in the form, or we could override the form's __getitem__() to ignore get_bound_field() and instead use MyBoundField directly.

Here's an example of an override, which is mostly a copy of the original source, except for the MyBoundField line:

class MyForm(forms.Form):
    label_attrs = {'class': 'my-class'}
    
    def __getitem__(self, name):
        """Return a MyBoundField with the given name."""
        try:
            field = self.fields[name]
        except KeyError:
            ...  # left out for clarity
        if name not in self._bound_fields_cache:
            self._bound_fields_cache[name] = MyBoundField(
                form=self, field=field, name=name, label_attrs=self.label_attrs)
        return self._bound_fields_cache[name]

In the end, this all seems like a lot of trouble for a bit of styling.

标点 2024-07-18 10:32:18

尝试 django-widget-tweaks

$ pip install django-widget-tweaks

将其添加到项目 settings.py 文件中的 INSTALLED_APPS :

INSTALLED_APPS += [
'widget_tweaks',
]

使用add_label_class过滤器:

{% load widget_tweaks %}
{{ form.your_field|add_label_class:"label" }}
 # equal to
<label class="label" for="{{ form.your_field.id_for_label }}">{{ form.your_field.label }}</label>

阅读文档以获取更多信息和功能。

Try django-widget-tweaks

$ pip install django-widget-tweaks

Add it to INSTALLED_APPS in your projects settings.py file:

INSTALLED_APPS += [
'widget_tweaks',
]

Use add_label_class filter:

{% load widget_tweaks %}
{{ form.your_field|add_label_class:"label" }}
 # equal to
<label class="label" for="{{ form.your_field.id_for_label }}">{{ form.your_field.label }}</label>

Read the Document to get more information and functions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文