我有一个ModelForm,它包含一个使用RadioSelect小部件的ModelChoiceField。
class MyAForm(forms.ModelForm):
one_property = models.ModelChoiceField(
widget=forms.RadioSelect,
queryset=MyBModel.objects.filter(visible=True),
empty_label=None)
class Meta:
model = MyAModel
我想在单选按钮旁边显示MyBModel上的属性。我会在ModelChoiceField的子类上覆盖label_from_instance
但是这不允许我做我想要的,因为我希望单选按钮出现在一个表中,每个选择项都有一行。
所以在我的模板中的某个地方,我想要像......
{% for field in form.visible_fields %}
{% if field.name == "one_property" %}
<table>
{% for choice in field.choices %}
<tr>
<td><input value="{{choice.id}}" type="radio" name="one_property" />{{choice.description}}</td>
<td><img src="{{choice.img_url}}" /></td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endfor %}
不幸的是,field.choices返回对象id和标签的元组,而不是查询集中的实例。
是否有一种简单的方法可以获取ModelChoiceField在模板中使用的选项实例?
答案 0 :(得分:12)
在深入研究ModelChoiceField的django源代码后,我发现它有一个属性“queryset”。
我能够使用像...这样的东西。
{% for field in form.visible_fields %}
{% if field.name == "one_property" %}
<table>
{% for choice in field.queryset %}
<tr>
<td><input value="{{choice.id}}" type="radio" name="one_property" />{{choice.description}}</td>
<td><img src="{{choice.img_url}}" /></td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endfor %}
答案 1 :(得分:4)
我想做一些与OP的问题几乎完全相同的事情(表格和所有内容),Django缺乏合作也同样感到沮丧,同样最终深入研究了我自己的实现。我提出的内容与接受的答案略有不同,我更喜欢它,因为我在模板中使用了一个简单的{{ form.as_table }}
,并且不想不必要地遍历visible_fields
或者在我的模板中硬编码一个单选按钮,它看起来与Django当前的实现类似(可能会改变)。这就是我做的事情:
Django的RadioSelect
窗口小部件使用RadioFieldRenderer
生成generator RadioInputs
,用于执行渲染单选按钮的实际工作。 RadioSelect
似乎有一个未记录的功能,您可以在其中传递与此默认值不同的渲染器,因此您可以将这两个渲染器子类化以获得OP所需的内容。
from django import forms
from django.utils.safestring import mark_safe
class CustomTableRadioInput(forms.widgets.RadioInput):
# We can override the render method to display our table rows
def render(self, *args, **kwargs):
# default_html will hold the normally rendered radio button
# which we can then use somewhere in our table
default_html = super(CustomTableRadioInput, self).render(*args, **kwargs)
# Do whatever you want to the input, then return it, remembering to use
# either django.utils.safestring.mark_safe or django.utils.html.format_html
# ...
return mark_safe(new_html)
class CustomTableFieldRenderer(forms.widget.RadioFieldRenderer):
# Here we just need to override the two methods that yield RadioInputs
# and make them yield our custom subclass instead
def __iter__(self):
for i, choice in enumerate(self.choices):
yield CustomTableRadioInput(self.name, self.value,
self.attrs.copy(), choice, i)
def __getitem__(self, idx):
choice = self.choices[idx] # Let the IndexError propogate
return CustomTableRadioInput(self.name, self.value,
self.attrs.copy(), choice, idx)
完成后,我们只需要告诉RadioSelect
小部件,只要我们在表单代码中的某处调用它,就可以使用我们的自定义渲染器:
...
radio = forms.ChoiceField(widget=forms.RadioSelect(renderer=CustomTableFieldRenderer),
choices=...)
...
就是这样!
请注意,要在模板中使用它,您可能希望循环遍历字段而不是直接调用它,即:
<table>
<tbody>
{% for tr in form.radio %}
<tr>{{ tr }}</tr>
{% endfor %}
</tbody>
</table>
而不是:
<table>
<tbody>{{ form.radio }}</tbody>
</table>
如果您执行后者,它将尝试将您的表格元素包装在<ul><li>...</li></ul>
。
答案 2 :(得分:2)
通常你不需要实际的对象,而是它的再现。
考虑以下代码:
class LabelledHiddenWidget(forms.HiddenInput):
def __init__(self, get_object, *args, **kwargs):
super(LabelledHiddenWidget, self).__init__(*args, **kwargs)
self.get_object = get_object
def render(self, name, value, attrs=None):
s = super(LabelledHiddenWidget, self).render(name, value, attrs)
if value:
s += SafeUnicode("<span>%s</span>" % self.get_object(value))
return s
然后你可以像这样使用它:
class SomeForm(forms.Form):
object = forms.ModelChoiceField(
SomeModel.objects.all(),
widget=LabelledHiddenWidget(get_object=lambda id: get_object_or_404(SomeModel, id=id)))
然后在模板代码中,{{ form.object }}
将输出一个带有对象id的隐藏字段,并与某个标签连接。当然,你的SomeModel应该实现__unicode__
或其他一些返回一个漂亮的,可读的标签的方法。