问题描述
如何将 CheckboxSelectMultiple
生成的复选框按相关模型分组?
How can I group checkboxes produced by CheckboxSelectMultiple
by a related model?
最好通过示例来证明这一点.
This is best demonstrated by example.
models.py:
class FeatureCategory(models.Model):
name = models.CharField(max_length=30)
class Feature(models.Model):
name = models.CharField(max_length=30)
category = models.ForeignKey(FeatureCategory)
class Widget(models.Model):
name = models.CharField(max_length=30)
features = models.ManyToManyField(Feature, blank=True)
forms.py:
class WidgetForm(forms.ModelForm):
features = forms.ModelMultipleChoiceField(
queryset=Feature.objects.all(),
widget=forms.CheckboxSelectMultiple,
required=False
)
class Meta:
model = Widget
views.py:
def edit_widget(request):
form = WidgetForm()
return render(request, 'template.html', {'form': form})
template.html:
{{ form.as_p }}
以上产生以下输出:
[] Widget 1
[] Widget 2
[] Widget 3
[] Widget 1
[] Widget 2
我希望功能复选框按功能类别分组(基于 ForeignKey
):
What I would like is for the feature checkboxes to be grouped by feature category (based on the ForeignKey
):
Category 1:
[] Widget 1
[] Widget 2
[] Widget 3
Category 2:
[] Widget 1
[] Widget 2
我怎样才能做到这一点?我曾尝试使用 {% regroup %}
模板标签无济于事.
How can I achieve this? I have tried using the {% regroup %}
template tag to no avail.
非常感谢任何建议.
谢谢.
推荐答案
您必须编写自定义 CheckboxSelectMultiple
小部件.使用 snippet 我尝试通过添加 CheckboxSelectMultiple
字段使 CheckboxSelectMultiple
字段可迭代code>category_name 作为字段 attrs
中的属性.这样我以后就可以在模板中使用 regroup
标签.
You have to write the custom CheckboxSelectMultiple
widget. Using the snippet I have tried make the CheckboxSelectMultiple
field iterable by adding the category_name
as an attribute in field attrs
. So that I can use regroup
tag in template later on.
下面的代码是根据你的需要从片段中修改的,显然这段代码可以做得更干净、更通用,但目前它不是通用的.
The below code is modified from snippet according to your need, obviously this code can be made more cleaner and more generic, but at this moment its not generic.
forms.py
from django import forms
from django.forms import Widget
from django.forms.widgets import SubWidget
from django.forms.util import flatatt
from django.utils.html import conditional_escape
from django.utils.encoding import StrAndUnicode, force_unicode
from django.utils.safestring import mark_safe
from itertools import chain
import ast
from mysite.models import Widget as wid # your model name is conflicted with django.forms.Widget
from mysite.models import Feature
class CheckboxInput(SubWidget):
"""
An object used by CheckboxRenderer that represents a single
<input type='checkbox'>.
"""
def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value
self.attrs = attrs
self.choice_value = force_unicode(choice[1])
self.choice_label = force_unicode(choice[2])
self.attrs.update({'cat_name': choice[0]})
self.index = index
def __unicode__(self):
return self.render()
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'<label%s>%s %s</label>' % (label_for, self.tag(), choice_label))
def is_checked(self):
return self.choice_value in self.value
def tag(self):
if 'id' in self.attrs:
self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index)
final_attrs = dict(self.attrs, type='checkbox', name=self.name, value=self.choice_value)
if self.is_checked():
final_attrs['checked'] = 'checked'
return mark_safe(u'<input%s />' % flatatt(final_attrs))
class CheckboxRenderer(StrAndUnicode):
def __init__(self, name, value, attrs, choices):
self.name, self.value, self.attrs = name, value, attrs
self.choices = choices
def __iter__(self):
for i, choice in enumerate(self.choices):
yield CheckboxInput(self.name, self.value, self.attrs.copy(), choice, i)
def __getitem__(self, idx):
choice = self.choices[idx] # Let the IndexError propogate
return CheckboxInput(self.name, self.value, self.attrs.copy(), choice, idx)
def __unicode__(self):
return self.render()
def render(self):
"""Outputs a <ul> for this set of checkbox fields."""
return mark_safe(u'<ul>
%s
</ul>' % u'
'.join([u'<li>%s</li>'
% force_unicode(w) for w in self]))
class CheckboxSelectMultipleIter(forms.CheckboxSelectMultiple):
"""
Checkbox multi select field that enables iteration of each checkbox
Similar to django.forms.widgets.RadioSelect
"""
renderer = CheckboxRenderer
def __init__(self, *args, **kwargs):
# Override the default renderer if we were passed one.
renderer = kwargs.pop('renderer', None)
if renderer:
self.renderer = renderer
super(CheckboxSelectMultipleIter, self).__init__(*args, **kwargs)
def subwidgets(self, name, value, attrs=None, choices=()):
for widget in self.get_renderer(name, value, attrs, choices):
yield widget
def get_renderer(self, name, value, attrs=None, choices=()):
"""Returns an instance of the renderer."""
choices_ = [ast.literal_eval(i[1]).iteritems() for i in self.choices]
choices_ = [(a[1], b[1], c[1]) for a, b, c in choices_]
if value is None: value = ''
str_values = set([force_unicode(v) for v in value]) # Normalize to string.
if attrs is None:
attrs = {}
if 'id' not in attrs:
attrs['id'] = name
final_attrs = self.build_attrs(attrs)
choices = list(chain(choices_, choices))
return self.renderer(name, str_values, final_attrs, choices)
def render(self, name, value, attrs=None, choices=()):
return self.get_renderer(name, value, attrs, choices).render()
def id_for_label(self, id_):
if id_:
id_ += '_0'
return id_
class WidgetForm(forms.ModelForm):
features = forms.ModelMultipleChoiceField(
queryset=Feature.objects.all().values('id', 'name', 'category__name'),
widget=CheckboxSelectMultipleIter,
required=False
)
class Meta:
model = wid
然后在模板中:
{% for field in form %}
{% if field.name == 'features' %}
{% regroup field by attrs.cat_name as list %}
<ul>
{% for el in list %}
<li>{{el.grouper}}
<ul>
{% for e in el.list %}
{{e}} <br />
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
{% else %}
{{field.label}}: {{field}}
{% endif %}
{% endfor %}
结果:我在类别表中添加了国家/地区名称,在功能表中添加了城市名称,因此在模板中我能够根据国家/地区(类别)重新组合城市(功能)
Results:I added countries name in category table, and cities name in features table so in template I was able to regroup the cities (features) according to country (category)
这篇关于Django模板中的分组CheckboxSelectMultiple的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!