【问题标题】:How do I create a Django form that displays a checkbox label to the right of the checkbox?如何创建一个在复选框右侧显示复选框标签的 Django 表单?
【发布时间】:2010-10-09 00:09:09
【问题描述】:

当我定义一个类似这样的 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 成为错误答案。

[编辑]

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

【问题讨论】:

    标签: python django checkbox


    【解决方案1】:

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

    checkbox.css

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

    forms.py

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

    模板.html

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

    优点:

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

    缺点:

    • HTML 不反映演示文稿(但已经足够了!)
    • 您的前端人员可能会抱怨

    【讨论】:

    • 你也可以把这个css sn -p 添加到你当前的css文件中,你就不需要使用{{ form.media }}
    【解决方案2】:

    我从 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 %}
    

    【讨论】:

      【解决方案3】:

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

      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
      

      仅当小部件是CheckboxInput 的子类时,Django 才使用左侧位置

      【讨论】:

        【解决方案4】:

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

        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 }}
        • 比我计划在星期六做的工作要多;-)

        【讨论】:

          【解决方案5】:

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

          {% 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 扫描它的字符串参数,找到成对的

          优点:

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

          缺点:

          1. 过滤器代码需要测试标签和输入字段名称的令人兴奋的值。
          2. 可能有什么东西可以做得更好更快。
          3. 比我计划在星期六做的工作要多。

          【讨论】:

          • 能否提供 pretty_checkbox 的代码?我自己的首选标记是
          • 我现在手头没有代码。如果我记得的话,这不是很复杂——它只是一个提供简短 HTML sn-p 的函数,如果输入是复选框,则交换标签和输入。标记是由 Django 生成的,所以你可能有任何偏好都可能无关紧要。
          • 你一定是在开玩笑。这在 HTML 中非常简单。所以我用 Django 来搞事情,因为我是一个真正的程序员?? Django 检查表单已损坏。纯粹而简单。
          • 在 Smalltalk 中更简单。
          【解决方案6】:

          这是我想出的一个解决方案(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)
          

          更多关于自定义模板标签的信息here

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

          优点:

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

          缺点:

          1. 可能有什么东西可以做得更好更快。
          2. 客户不太可能愿意为此付出所有时间,只是为了将标签移到右侧...

          【讨论】:

          • 我喜欢这个答案。实现很简单,这总是一个优点。仅通过阅读模板就可以清楚地知道发生了什么。唯一的缺点(在我的偏见和公认的懒惰观点中)是我的解决方案使用更短的模板,并且更接近您最初期望 Django 所做的事情。
          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-03-10
            • 2022-11-18
            • 2013-07-31
            • 1970-01-01
            • 2021-08-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多