我希望能够以我的django形式提取不同的信息:
这是我的表格:
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
class InstanceForm(ModelForm):
class Meta:
model = models.BaseAsset
widgets = {
'labels': LabelIconCheckboxSelectMultiple()
}
模特:
class AssetClass(models.Model):
default_labels = models.ManyToManyField(Label, null=True, blank=True)
pass
M2M参考字段
class Label(models.Model):
explanation = models.CharField(null=True, max_length=63)
svgpreview = models.CharField(null=True, max_length=31)
def __unicode__(self):
return unicode(self.explanation)
pass
现在,{{ form.as_p }}
生成的HTML代码如下:
<li><label for="id_labels_0"><input type="checkbox" name="labels" value="1" id="id_labels_0" /> Consult owner before using</label></li>
<li><label for="id_labels_1"><input type="checkbox" name="labels" value="2" id="id_labels_1" /> This item is broken</label></li>
这意味着它显然正在使用模型'Label'的__unicode__
渲染。如何在“选择”小部件中更改该行为,以便它使用不同的函数来填充它的选择?我试图以合理的便携方式在复选框旁打印'<img src="{{label.svgpreview}}" alt="{{label.explanation}}"...>'
吗?
答案 0 :(得分:5)
您将覆盖forms.widgets.CheckboxSelectMultiple
类:
这是CheckboxSelectMultiple类及其渲染函数:
class CheckboxSelectMultiple(SelectMultiple):
def render(self, name, value, attrs=None, choices=()):
if value is None: value = []
has_id = attrs and 'id' in attrs
final_attrs = self.build_attrs(attrs, name=name)
output = [u'<ul>']
# Normalize to strings
str_values = set([force_unicode(v) for v in value])
for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
# If an ID attribute was given, add a numeric index as a suffix,
# so that the checkboxes don't all have the same ID attribute.
if has_id:
final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
label_for = u' for="%s"' % final_attrs['id']
else:
label_for = ''
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
option_value = force_unicode(option_value)
rendered_cb = cb.render(name, option_value)
option_label = conditional_escape(force_unicode(option_label))
output.append(u'<li><label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label))
output.append(u'</ul>')
return mark_safe(u'\n'.join(output))
那你要做什么:
class MyCheckboxSelectMultiple(CheckboxSelectMultiple):
def render(self, name, value, attrs=None, choices=()):
#put your code to have custom checkbox control with icon
#...
output.append(u'<li><label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label)) # especially you will be working on this line
#...
然后,您使用widgets=CheckboxSelectMultiple()
的位置将变为widgets=MyCheckboxSelectMultiple()
答案 1 :(得分:3)
阅读django.forms.models.ModelChoiceField
给出了一个提示:
# this method will be used to create object labels by the QuerySetIterator.
# Override it to customize the label.
def label_from_instance(self, obj):
"""
This method is used to convert objects into strings; it's used to
generate the labels for the choices presented by this object. Subclasses
can override this method to customize the display of the choices.
"""
return smart_unicode(obj)
好的,但是如何在ModelForm
的每个实例中覆盖它 - 这会在django.forms
考虑以下代码:
class InstanceForm(ModelForm):
class Meta:
model = models.BaseAsset
widgets = {
'labels': forms.CheckboxSelectMultiple()
}
def __init__(self, *args, **kwargs):
def new_label_from_instance(self, obj):
return obj.svgpreview
super(InstanceForm, self).__init__(*args, **kwargs)
funcType = type(self.fields['labels'].label_from_instance)
self.fields['labels'].label_from_instance = funcType(new_label_from_instance, self.fields['labels'], forms.models.ModelMultipleChoiceField)
这有点令人毛骨悚然 - 基本上,这是一个更为古怪的实现: Override a method at instance level
请阅读参考线程中的注释,以了解为什么这通常是一个坏主意..
答案 2 :(得分:0)
您不必执行“令人毛骨悚然”的实例级覆盖,以便正确利用documented django.forms.models.ModelChoiceField.label_from_instance()方法。
以原始帖子中的AssetClass
和Label
个对象为基础:
class AssetSvgMultiField(forms.ModelMultipleChoiceField):
"""
Custom ModelMultipleChoiceField that labels instances with their svgpreview.
"""
def label_from_instance(self, obj):
return obj.svgpreview
class InstanceForm(forms.ModelForm):
default_labels = AssetSvgMultiField(queryset=Label.objects.all())
class Meta:
model = models.AssetClass
widgets = {
'default_labels': forms.CheckboxSelectMultiple()
}
答案 3 :(得分:0)
这在Django文档中有解释: https://docs.djangoproject.com/en/1.9/ref/forms/fields/#django.forms.ModelChoiceField.to_field_name
你可以看到ModelChoiceField类在这里调用字段上的方法: https://github.com/django/django/blob/1155843a41af589a856efe8e671a796866430049/django/forms/models.py#L1174
如果您未明确覆盖choices
,那么您的代码可能如下所示:
class RectificationAssetMultiField(forms.ModelMultipleChoiceField):
def label_from_instance(self, obj):
return '[{0.pk}] {0.label} ({0.location})'.format(obj)
class RectificationForm(forms.ModelForm):
items = RectificationAssetMultiField(
required=False,
queryset=InspectionItem.objects.all(),
widget=forms.CheckboxSelectMultiple,
label="Non-compliant Assets"
)
class Meta:
model = Rectification
fields = ('ref', 'items', 'status')
请注意,只有在您未直接设置choices
时才能使用此功能(请参阅上述网址中的_get_choices
)。
如果你想要覆盖选择(为了比查询集更有效的结果,或者更好地表达为ValuesList的东西),那么你会有这样的事情:
class RectificationAssetMultiField(forms.ModelMultipleChoiceField):
def label_from_instance(self, obj):
return '[{0.pk}] {0.label} ({0.location})'.format(obj)
class RectificationForm(forms.ModelForm):
items = RectificationAssetMultiField(
required=False,
queryset=InspectionItem.objects.none(),
widget=forms.CheckboxSelectMultiple,
label="Non-compliant Assets"
)
def __init__(self, *args, **kwargs):
super(RectificationForm, self).__init__(*args, **kwargs)
self.fields['items'].choices = (InspectionItem.objects
.active()
.noncompliant()
.filter(property_id=self.instance.property_id)
.values_list('pk', 'label') # pass a key value pair
)
class Meta:
model = Rectification
fields = ('ref', 'items', 'status')
答案 4 :(得分:-1)
如果您不喜欢该渲染,请不要使用{{ form.as_p }}
。
在表单上循环:
<form action="/contact/" method="post">
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</div>
{% endfor %}
<p><input type="submit" value="Send message" /></p>
</form>
然后您可以自由使用您想要的任何HTML。
来自:https://docs.djangoproject.com/en/dev/topics/forms/#looping-over-the-form-s-fields