Django模板中的Grouped CheckboxSelectMultiple
发布时间:2020-12-20 10:32:00 所属栏目:Python 来源:网络整理
导读:如何通过相关模型对CheckboxSelectMultiple生成的复选框进行分组? 最好通过示例来说明这一点. models.py: class FeatureCategory(models.Model): name = models.CharField(max_length=30)class Feature(models.Model): name = models.CharField(max_length
如何通过相关模型对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小部件.使用
snippet我尝试通过在field attrs中添加category_name作为属性来使CheckboxSelectMultiple字段可迭代.这样我以后就可以在模板中使用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,choices): self.name,self.value,self.attrs = name,attrs self.choices = choices def __iter__(self): for i,choice in enumerate(self.choices): yield CheckboxInput(self.name,self.attrs.copy(),i) def __getitem__(self,idx): choice = self.choices[idx] # Let the IndexError propogate return CheckboxInput(self.name,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%sn</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,choices=()): for widget in self.get_renderer(name,choices): yield widget def get_renderer(self,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,choices=()): return self.get_renderer(name,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 %} 结果:我在类别表中添加了国家/地区名称,并在功能表中添加了城市名称,因此在模板中我能够根据国家(类别)重新组合城市(功能) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |