37

当我定义一个类似于这样的 Django 表单类时:

def class MyForm(forms.Form):
    check = forms.BooleanField(required=True, label="Check this")

它扩展为如下所示的 HTML:

<form action="." id="form" method=POST>
<p><label for="check">Check this:</label> <input type="checkbox" name="check" id="check" /></p>
<p><input type=submit value="Submit"></p>
</form>

我希望复选框输入元素在复选框后面有一个标签,而不是相反。有没有办法说服 Django 这样做?

[编辑]

感谢 Jonas 的回答 - 虽然它解决了我询问的问题(复选框标签呈现在复选框的右侧),但它引入了一个新问题(所有小部件标签都呈现在其小部件的右侧......)

我想避免覆盖 _html_output() 因为它显然不是为它设计的。我想出的设计是在 Field 类中实现一个字段 html 输出方法,覆盖布尔字段的方法并在 _html_output() 中使用该方法。遗憾的是,Django 开发人员选择了不同的方式,我希望尽可能地在现有框架内工作。

CSS 听起来是一种不错的方法,只是我对 CSS 的了解不够多,无法解决这个问题,甚至无法决定我是否喜欢这种方法。此外,我更喜欢仍然类似于最终输出的标记,至少在渲染顺序上是这样。

此外,由于为任何特定标记拥有多个样式表是合理的,因此在 CSS 中执行此操作可能意味着必须为多种样式执行多次,这几乎使 CSS 成为错误答案。

[编辑]

好像我在下面回答我自己的问题。如果有人对如何做到这一点有更好的想法,请不要害羞。

4

7 回答 7

33

这是我提出的解决方案(Django v1.1):

{% load myfilters %}

[...]

{% for field in form %}
    [...]
    {% if field.field.widget|is_checkbox %}
      {{ field }}{{ field.label_tag }}
    {% else %}
      {{ field.label_tag }}{{ field }}
    {% endif %}
    [...]
{% endfor %}

您需要创建一个自定义模板标签(在本例中为“myfilters.py”文件),其中包含如下内容:

from django import template
from django.forms.fields import CheckboxInput

register = template.Library()

@register.filter(name='is_checkbox')
def is_checkbox(value):
    return isinstance(value, CheckboxInput)

有关自定义模板标签的更多信息,请点击此处

编辑:本着提问者自己的回答的精神:

好处:

  1. 没有使用 CSS。
  2. 标记最终看起来像它应该的样子。
  3. 我没有破解 Django 内部。(但不得不看很多)
  4. 该模板很好,紧凑且惯用。
  5. 无论标签和输入字段名称的确切值如何,过滤器代码都能很好地发挥作用。

缺点:

  1. 可能有什么地方可以做得更好更快。
  2. 客户不太可能愿意为此付出所有时间,只是为了将标签向右移动……
于 2010-01-11T22:06:27.673 回答
15

我从 romkyns 那里得到了答案,让它更笼统一些

def field_type(field, ftype):
    try:
        t = field.field.widget.__class__.__name__
        return t.lower() == ftype
    except:
        pass
    return False

这样您可以直接使用字符串检查小部件类型

{% if field|field_type:'checkboxinput' %}
    <label>{{ field }} {{ field.label }}</label>
{% else %}
    <label> {{ field.label }} </label> {{ field }}
{% endif %}
于 2010-02-22T03:52:58.357 回答
12

所有提出的解决方案都涉及模板修改,这在性能方面通常相当低效。这是一个可以完成这项工作的自定义小部件:

from django import forms
from django.forms.fields import BooleanField
from django.forms.util import flatatt
from django.utils.encoding import force_text
from django.utils.html import format_html
from django.utils.translation import ugettext as _


class PrettyCheckboxWidget(forms.widgets.CheckboxInput):
    def render(self, name, value, attrs=None):
        final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
        if self.check_test(value):
            final_attrs['checked'] = 'checked'
        if not (value is True or value is False or value is None or value == ''):
            final_attrs['value'] = force_text(value)
        if 'prettycheckbox-label' in final_attrs:
            label = _(final_attrs.pop('prettycheckbox-label'))
        else:
            label = ''
        return format_html('<label for="{0}"><input{1} /> {2}</label>', attrs['id'], flatatt(final_attrs), label)


class PrettyCheckboxField(BooleanField):
    widget = PrettyCheckboxWidget
    def __init__(self, *args, **kwargs):
        if kwargs['label']:
            kwargs['widget'].attrs['prettycheckbox-label'] = kwargs['label']
            kwargs['label'] = ''
        super(PrettyCheckboxField, self).__init__(*args, **kwargs)


# usage in form
class MyForm(forms.Form):
    my_boolean = PrettyCheckboxField(label=_('Some label'), widget=PrettyCheckboxWidget())

我在一个额外的文件中有PrettyCheckboxWidgetPrettyCheckboxField,因此可以在需要的地方导入它们。如果您不需要翻译,您可以删除 ugettext 部分。此代码适用于Django 1.5,未针对较低版本进行测试。

好处:

  • 高性能,无需模板修改
  • 易于用作自定义小部件

缺点:

  • "as_table" 在第二列中呈现复选框 + 标签
  • 模板内的 {{ field.label }} 为空。标签改为绑定到 {{ field }}
  • 比我计划在星期六做的工作要多;-)
于 2013-03-09T07:18:55.620 回答
3

我知道用户排除了 CSS,但考虑到最重要的答案需要大约半小时的工作来完成这么小的事情,但知道这些细节在网站上很重要,我会选择 CSS 解决方案。

复选框.css

input[type="checkbox"] {
    float: left;
    margin-right: 10px;
    margin-top: 4px;
}

表格.py

class MyForm(forms.ModelForm):
    # ...
    class Media:
    css = {
        'all': 'checkbox.css',
    }

模板.html

{{ form.media }}
{{ form.as_p }}

好处:

  • 快速地!
  • 不使用模板标签(只是form.as_p
  • 没有新的该死的小部件
  • CSS 文件自动包含在每个表单中

缺点:

  • HTML 不反映演示文稿(但足够好!)
  • 你的前端人员可能会抱怨
于 2013-07-23T11:27:15.230 回答
2

这就是我最终做的事情。我写了一个自定义模板字符串过滤器来切换标签。现在,我的模板代码如下所示:

{% load pretty_forms %}
<form action="." method="POST">
{{ form.as_p|pretty_checkbox }}
<p><input type="submit" value="Submit"></p>
</form>

与普通 Django 模板的唯一区别是添加了 {% load %} 模板标签和pretty_checkbox过滤器。

这是pretty_checkbox的一个实用但丑陋的实现——这段代码没有任何错误处理,它假设 Django 生成的属性以非常特定的方式格式化,在你的代码中使用这样的东西是个坏主意:

from django import template
from django.template.defaultfilters import stringfilter
import logging

register=template.Library()

@register.filter(name='pretty_checkbox')
@stringfilter
def pretty_checkbox(value):
    # Iterate over the HTML fragment, extract <label> and <input> tags, and
    # switch the order of the pairs where the input type is "checkbox".
    scratch = value
    output = ''
    try:
        while True:
            ls = scratch.find('<label')
            if ls > -1:
                le = scratch.find('</label>')
                ins = scratch.find('<input')
                ine = scratch.find('/>', ins)
                # Check whether we're dealing with a checkbox:
                if scratch[ins:ine+2].find(' type="checkbox" ')>-1:
                    # Switch the tags
                    output += scratch[:ls]
                    output += scratch[ins:ine+2]
                    output += scratch[ls:le-1]+scratch[le:le+8]
                else:
                    output += scratch[:ine+2]
                scratch = scratch[ine+2:]
            else:
                output += scratch
                break
    except:
        logging.error("pretty_checkbox caught an exception")
    return output

pretty_checkbox扫描它的字符串参数,找到成对的 <label> 和 <input> 标签,如果 <input> 标签的类型是“checkbox”,则切换它们。它还去除了标签的最后一个字符,恰好是 ':' 字符。

好处:

  1. 没有使用 CSS。
  2. 标记最终看起来像它应该的样子。
  3. 我没有破解 Django 内部。
  4. 该模板很好,紧凑且惯用。

缺点:

  1. 过滤器代码需要测试标签和输入字段名称的令人兴奋的值。
  2. 可能有什么地方可以做得更好更快。
  3. 比我计划在星期六做的工作要多。
于 2009-02-22T06:25:08.273 回答
2

在 Django admin 中更改复选框位置可能非常棘手,但幸运的是,有一个使用自定义小部件的简单解决方案:

from django.forms.widgets import Widget, CheckboxInput, boolean_check

class RightCheckbox(Widget):
    render = CheckboxInput().render

    def __init__(self, attrs=None, check_test=None):
        super(RightCheckbox, self).__init__(attrs)
        self.check_test = boolean_check if check_test is None else check_test

Django 仅在小部件是CheckboxInput

于 2015-02-15T19:11:24.823 回答
1

输入和标签的顺序是通过表单的 normal_row 参数提供的,复选框没有不同的行模式。所以有两种方法可以做到这一点(在 0.96 版本中):
1. 覆盖表单的 _html_output
2. 使用 CSS 更改标签和复选框的位置

于 2009-02-21T06:52:58.230 回答