我有一个位置模型和一个关联的表单,它将“位置”字段显示为一堆单选按钮(使用表单查询集来显示值)。位置数量很少,但它们需要是动态的。我想在每个单选框选项旁边显示位置描述,以便用户获得有关位置的更多信息。
假设这是一个单选按钮列表,这就是我希望它看起来的样子:
<> 东 - 该位置位于东。 <> 西边 - 这是西边的位置! <> 北 - 这是北位置
我有一个类似于以下的模型:
class Location(models.Models):
location = models.CharField(max_length=50)
description = models.TextField(blank=True, null=True)
以及这样的表单:
class LocationForm(forms.Form):
location = ExtraModelChoiceField(
widget=RadioSelect(renderer=ExtraHorizRadioRenderer),
queryset = models.Locations.objects.filter(active=True))
我似乎找不到渲染表单的好方法,因此我可以显示说明以及每个选择选项。我已经做了很多事情,但运气不太好。
我尝试解决(但还没有运气):
据我所知,通常如果在表单字段上提供查询集,Django 表单逻辑会将其转换为 tupal 的选择 tupal。每个“subtupal”都包含一个 id 和在渲染时显示的标签。我试图向这些“subtupals”添加第三个值,这将是一个描述。
我定义了一个自定义渲染器来水平显示我的单选按钮并传递我的自定义选择。
class ExtraHorizRadioRenderer(forms.RadioSelect.renderer):
def render(self):
return mark_safe(u'\n'.join([u'%s\n' % w for w in self]))
def __iter__(self):
for i, choice in enumerate(self.choices):
yield ExtraRadioInput(self.name, self.value,
self.attrs.copy(), choice, i)
def __getitem__(self, idx):
choice = self.choices[idx] # Let the IndexError propogate
return ExtraRadioInput(self.name, self.value,
self.attrs.copy(), choice, idx)
我已经重写了 Django RadioInput 类,因此我可以添加需要在单选按钮旁边显示的描述信息。
class ExtraRadioInput(forms.widgets.RadioInput):
def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value
self.attrs = attrs
self.choice_value = force_unicode(choice[0])
self.choice_label = force_unicode(choice[1])
self.choice_description = force_unicode(choice[2]) # <--- MY ADDITION; FAILS
self.index = index
def __unicode__(self):
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'<label%s>%s %s</label>' % (
label_for, self.tag(), choice_label))
def tag(self):
if 'id' in self.attrs:
self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index)
final_attrs = dict(self.attrs, type='radio', name=self.name,
value=self.choice_value)
if self.is_checked():
final_attrs['checked'] = 'checked'
return mark_safe(
u'<input%s /><span class="description">%s</span>' % \
(flatatt(final_attrs),self.choice_description )) # <--- MY ADDTIONS
我还重写了以下两个 Django 类,希望传递我修改后的选择 tupals。
class ExtraModelChoiceIterator(forms.models.ModelChoiceIterator ):
def choice(self, obj):
if self.field.to_field_name:
key = obj.serializable_value(self.field.to_field_name)
else:
key = obj.pk
if obj.description: # <-- MY ADDITIONS
description = obj.description
else:
description = ""
return (key, self.field.label_from_instance(obj),description)
class ExtraModelChoiceField(forms.models.ModelChoiceField):
def _get_choices(self):
if hasattr(self, '_choices'):
return self._choices
return ExtraModelChoiceIterator(self) # <-- Uses MY NEW ITERATOR
使用上面的方法,我似乎无法传递我的 3 值 tupal。我收到“元组索引超出范围”失败(在上面标记为“失败”的位置),表明不知何故我的 tupal 没有额外的值。
是否有人发现我的逻辑中存在缺陷,或者更普遍地有一种方法可以使用小部件在选项列表旁边显示描述?
感谢您的阅读。非常感谢任何评论。 乔
最佳答案
很抱歉回答我自己的问题,但我想我有办法做到这一点。和往常一样,它似乎比我以前做的要简单。重写扩展 ModelChoiceField 上的 label_from_instance 方法似乎允许我访问模型对象实例以便能够打印出额外的信息。
from django.utils.encoding import smart_unicode, force_unicode
class ExtraModelChoiceField(forms.models.ModelChoiceField):
def label_from_instance(self, obj):
return mark_safe(
"<span>%s</span><span class=\"desc\" id=\"desc_%s\">%s</span>" % (
mart_unicode(obj), obj.id, smart_unicode(obj.description),))
class HorizRadioRenderer(forms.RadioSelect.renderer):
# for displaying select options horizontally.
# https://wikis.utexas.edu/display/~bm6432/Django-Modifying+RadioSelect+Widget+to+have+horizontal+buttons
def render(self):
return mark_safe(u'\n'.join([u'%s\n' % w for w in self]))
class LocationForm(forms.Form):
location = ExtraModelChoiceField(widget=forms.RadioSelect(renderer=HorizRadioRenderer),
queryset=models.Location.objects.filter(active=True))
如果您知道更好的方法,我会很高兴看到它。否则,就必须这样做。谢谢阅读。希望这可以减轻我的挫败感。
乔
关于Django 渲染单选按钮选项以及来自模型的额外信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5558509/