【问题标题】:Customizing Django forms with RadioSelect widget使用 RadioSelect 小部件自定义 Django 表单
【发布时间】:2012-07-18 08:38:57
【问题描述】:

所以我使用 jQuery UI 来skin the radio buttons,但我无法让 Django 以必须完成的方式呈现我的表单。

我需要这个结构:

<table>
    <tr>
        <td><label for="notify_new_friends">Notify when new friends join</label></td>
        <td class="radio">
            <input type="radio" name="notify_new_friends" id="notify_new_friends_immediately" value="1" checked="checked"/><label for="notify_new_friends_immediately">Immediately</label>
            <input type="radio" name="notify_new_friends" id="notify_new_friends_never" value="0"/><label for="notify_new_friends_never">Never</label>
        </td>
    </tr>
</table>

总结一下,我需要一个类(单选)中的单选按钮,它们有一个input 和一个label for

当我使用 {{ profile_form.notify_new_friends }} 在模板中呈现表单时,我得到以下信息:

<ul>
    <li><label for="id_notify_new_friends_0"><input type="radio" id="id_notify_new_friends_0" value="0" name="notify_new_friends" /> Immediately</label></li>
    <li><label for="id_notify_new_friends_1"><input type="radio" id="id_notify_new_friends_1" value="1" name="notify_new_friends" /> Never</label></li>
</ul>

这正是我想要的,除了列表部分。所以我尝试循环遍历它,这给了我不同格式的标签:

{% for item in profile_form.notify_new_friends %}
    {{ item }}
{% endfor %}

这给了我:

<label><input type="radio" name="notify_new_friends" value="0" /> Immediately</label>
<label><input type="radio" name="notify_new_friends" value="1" /> Never</label>

所以这里的问题是它停止使用label for 并开始使用label 来包装它。

我也尝试过这样做,但是 labellabel_tag 不渲染任何东西。

{{ profile_form.notify_new_friends.0 }}
{{ profile_form.notify_new_friends.0.label_tag }}
{{ profile_form.notify_new_friends.0.label }}

那么有谁知道我该如何正确渲染它!?

仅供参考,这是我的 forms.py:

self.fields['notify_new_friends'] = forms.ChoiceField(label='Notify when new friends join', widget=forms.RadioSelect, choices=NOTIFICATION_CHOICES)

【问题讨论】:

  • @AlexanderA.Sosnovskiy 是的,但我错过了 choice_label 来获得标签标题,但不幸的是,{{ radio.tag }} 没有为我提供 ID,因此我不能这样做 label for我需要做的。
  • 字段 id 是自动生成的尝试,{{ field.auto_id }}
  • @ArgsKwargs 我可能误解了如何使用它,但不,这并没有给我任何渲染。

标签: django django-forms django-templates


【解决方案1】:

在我的代码中,我发现将小部件从

forms.RadioSelect

forms.RadioSelect(attrs={'id': 'value'})

神奇地导致生成的tag 值包含id 属性和附加的项目索引。如果你使用

{% for radio in form.foo %}
    <li>
        {{ radio }}
    </li>
{% endfor %}

在表单中,您会得到一个包裹在 input 周围的 label。如果您想要更传统的input 后跟label,您需要这样做:

{% for radio in form.value %}
    <li>
        {{ radio.tag }}
        <label for="value_{{ forloop.counter0 }}">{{ radio.choice_label }}</label>
    </li>
{% endfor %}

【讨论】:

    【解决方案2】:

    不幸的是,这比应有的复杂得多,看来您至少需要覆盖 2 个类:RadioRendererRadioInput。以下内容应该可以帮助您入门,但您可能需要稍作调整。

    首先创建一个自定义单选按钮输入小部件。我们重写 render 方法的唯一目的是摆脱 Django 强制执行的烦人结构 (&lt;label&gt;&lt;input /&gt;&lt;/label&gt;),而我们想要我们的 (&lt;label /&gt;&lt;input /&gt;):

    class CustomRadioInput(RadioInput):
        def render(self, name=None, value=None, attrs=None, choices=()):
            name = name or self.name
            value = value or self.value
            attrs = attrs or self.attrs
            if 'id' in self.attrs:
                label_for = ' for="%s_%s"' % (self.attrs['id'], self.index)
            else:
                label_for = ''
                choice_label = conditional_escape(force_unicode(self.choice_label))
                return mark_safe(u'%s<label%s>%s</label>' % (self.tag(), label_for, choice_label))
    

    现在我们需要覆盖 RadioRenderer 以便:

    1. 强制它使用我们的自定义单选输入小部件
    2. 删除 &lt;li&gt; 包装每个输入字段和 &lt;ul&gt; 包装所有输入字段:

    应该这样做:

    class RadioCustomRenderer(RadioFieldRenderer):
        def __iter__(self):
            for i, choice in enumerate(self.choices):
                yield CustomRadioInput(self.name, self.value, self.attrs.copy(), choice, i)
    
        def __getitem__(self, idx):
            choice = self.choices[idx]
            return CustomRadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
    
        def render(self):
            return mark_safe(u'%s' % u'\n'.join([u'%s' % force_unicode(w) for w in self]))
    

    最后指示 Django 使用自定义渲染器

    notify_new_friends = forms.ChoiceField(label='Notify when new friends join', widget=forms.RadioSelect(renderer=RadioCustomRenderer), choices=NOTIFICATION_CHOICES)
    

    请记住:这会输出单选按钮以及包含&lt;td&gt;,因此您需要在模板中围绕它构建一个表格,如下所示:

    <table>
      <tr>
        <td><label for="{{field.auto_id}}">{{field.label}}</label></td>
        <td>{{ field.errors }} {{field}}</td>
      </tr>
    </table>
    

    【讨论】:

    • 我同意,这方面的文档缺少 imo。虽然我同意大多数 Django 文档,但使用 shell 的 html 表单示例并不是最好的方法。
    【解决方案3】:

    如果有人偶然发现这个问题并且只想在没有 ul 的情况下呈现单选按钮:他们应该点击这个链接。

    https://docs.djangoproject.com/en/3.1/ref/forms/widgets/#selector-widgets

    以下示例。

    {% for radio in myform.beatles %}
        <div class="myradio">
        {{ radio }}
        </div>
    {% endfor %}
    

    【讨论】:

    • 终于找到了答案!谢谢。
    【解决方案4】:

    由于这似乎不是一个好方法,我选择使用 jQuery 重新排列生成的代码。

    // First remove the ul and li tags
    $('.radio ul').contents().unwrap();
    $('.radio li').contents().unwrap();
    // Then move the input to outside of the label
    $('.radio > label > input').each(function() {
        $(this).parent().before(this);
    });
    // Then apply the jQuery UI buttonset
    $( ".radio" ).buttonset();
    

    这使它从:

    <ul>
        <li><label for="id_notify_new_friends_0"><input type="radio" id="id_notify_new_friends_0" value="0" name="notify_new_friends" /> Immediately</label></li>
        <li><label for="id_notify_new_friends_1"><input type="radio" id="id_notify_new_friends_1" value="1" name="notify_new_friends" /> Never</label></li>
    </ul>
    

    到:

    <input type="radio" id="id_notify_new_friends_0" value="0" name="notify_new_friends" /><label for="id_notify_new_friends_0"> Immediately</label></li>
    <input type="radio" id="id_notify_new_friends_1" value="1" name="notify_new_friends" /><label for="id_notify_new_friends_1"> Never</label></li>
    

    我的 jQuery UI 样式工作正常。

    【讨论】:

    • 我认为如果有可能在没有 javascript 的情况下执行此操作,我们应该避免使用 js……尤其是在页面加载时 - 性能更差,SEO 更差。如果我们使用javascript进行操作,应该是动态操作,导致交互这样的原因。
    • 我不认为简单地重新排列页面上相同内容的 JavaScript 可能会影响 SEO。除此之外,此解决方案可能在 Django 管理员中运行良好,其中 SEO 无关紧要,控制 HTML 输出将需要覆盖多个 Django 类,如 tnajdek 的回答中所述。
    【解决方案5】:
    Try like this ,  I got it..
    
    from django.forms.widgets import RadioFieldRenderer
    from django.utils.encoding import force_unicode
    from django.utils.safestring import mark_safe
    
    
    class RadioCustomRenderer( RadioFieldRenderer ):
        def render( self ):
            return  mark_safe(u'%s' % u'\n'.join([u'%s'  % force_unicode(w) for w in self]))
    
    in form
    
    widgets = {
                'validity': forms.RadioSelect(renderer=RadioCustomRenderer),
            }
    

    【讨论】:

      猜你喜欢
      • 2011-11-29
      • 2013-04-03
      • 1970-01-01
      • 2011-06-12
      • 1970-01-01
      • 2014-12-30
      • 2011-07-23
      • 2011-06-10
      • 2010-12-12
      相关资源
      最近更新 更多