使用 RadioSelect 小部件自定义 Django 表单
Posted
技术标签:
【中文标题】使用 RadioSelect 小部件自定义 Django 表单【英文标题】:Customizing Django forms with RadioSelect widget 【发布时间】: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
来包装它。
我也尝试过这样做,但是 label
和 label_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)
【问题讨论】:
docs.djangoproject.com/en/dev/ref/forms/widgets/#radioselect - 你读过这个吗? @AlexanderA.Sosnovskiy 是的,但我错过了choice_label
来获得标签标题,但不幸的是, radio.tag
没有为我提供 ID,因此我不能这样做 label for
我需要做的。
字段 id 是自动生成的尝试, field.auto_id
@ArgsKwargs 我可能误解了如何使用它,但不,这并没有给我任何渲染。
【参考方案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 个类:RadioRenderer
和 RadioInput
。以下内容应该可以帮助您入门,但您可能需要稍作调整。
首先创建一个自定义单选按钮输入小部件。我们重写 render 方法的唯一目的是摆脱 Django 强制执行的烦人结构 (<label><input /></label>
),而我们想要我们的 (<label /><input />
):
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
以便:
-
强制它使用我们的自定义单选输入小部件
删除
<li>
包装每个输入字段和 <ul>
包装所有输入字段:
应该这样做:
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)
请记住:这会输出单选按钮以及包含<td>
,因此您需要在模板中围绕它构建一个表格,如下所示:
<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),
【讨论】:
以上是关于使用 RadioSelect 小部件自定义 Django 表单的主要内容,如果未能解决你的问题,请参考以下文章
使用包含 RadioSelect 小部件的 ModelChoiceField 在 ModelForm 的查询集中获取 request.user
如何将 CSS 类添加到 django RadioSelect 标签?