在Django模板中将CheckboxSelectMultiple分组

如何CheckboxSelectMultiple对相关模型产生的复选框进行分组?

这是最好的例子。

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)对功能复选框进行分组:

Category 1:

[] Widget 1

[] Widget 2

[] Widget 3

Category 2:

[] Widget 1

[] Widget 2

我该如何实现?我尝试使用{% regroup %}模板标签无济于事。

任何建议,不胜感激。

回答:

你必须编写自定义CheckboxSelectMultiple窗口小部件。通过使用代码段,我尝试通过将CheckboxSelectMultiplefield category_name作为属性添加到字段中以使字段可迭代attrs。这样我以后可以regroup在模板中使用标记。

下面的代码根据你的需要从代码段中进行了修改,显然可以使此代码更简洁,更通用,但是目前还不是通用的。

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>\n%s\n</ul>' % u'\n'.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 %}

结果:我在类别表中添加了国家名称,在功能表中添加了城市名称,因此在模板中,我能够根据国家(类别)对城市(功能)进行重新分组

以上是 在Django模板中将CheckboxSelectMultiple分组 的全部内容, 来源链接: utcz.com/qa/419016.html

回到顶部