2012-12-16 13 views
8

CheckboxSelectMultiple tarafından üretilen onay kutularını ilgili bir modelle nasıl gruplayabilirim?Gruplanmış Onay KutusuSelectÇokluda Django şablon

Bu örnekte en iyi şekilde gösterilmiştir.

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 }} 

yukarıdaki aşağıdaki çıktıyı üretir:

[] Widget 1 
[] Widget 2 
[] Widget 3 
[] Widget 1 
[] Widget 2 

Size özelliğin onay kutularını (ForeignKey dayalı) özelliği kategoriye göre gruplandırılmış edilecek olan için istiyorum Ne:

Category 1: 
    [] Widget 1 
    [] Widget 2 
    [] Widget 3 

Category 2: 
    [] Widget 1 
    [] Widget 2 

Bunu nasıl başarabilirim? {% regroup %} şablon etiketini boşuna kullanmaya çalıştım.

Herhangi bir tavsiye çok takdir edilmektedir.

Teşekkürler.

cevap

14

Özel CheckboxSelectMultiple widget'ını oluşturmanız gerekir. snippet kullanarak attrs alanında category_name özelliğini bir öznitelik olarak ekleyerek CheckboxSelectMultiple alanını yinelenebilir hale getirmeye çalıştım. Böylece daha sonra şablonda regroup etiketini kullanabilirim.

Aşağıdaki kod, gereksiniminize göre snippet'ten değiştirilmiştir, açıkçası bu kod daha temiz ve daha genel yapılabilir, ancak şu anda jenerik değil.

{% 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 %} 

Sonuçlar:

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 
şablonda Sonra

özellikleri tabloda ben kategori tablosunda ülkeler isim eklendi ve şehirler adı şablonuna ben yeniden toplamak mümkün oldu şehirler (özellikler) ülkeye göre (kategori)

enter image description here

+0

Yerleşik bir yol olacağını umuyordum, ama bu tam olarak aradığım şeyi yapıyor. Teşekkür ederim. – gjb

+0

Açılışınız :) –

+0

Bu kod hala çalışıyor mu? Sadece kullanmayı denedim ve ast ile ilgili hata mesajlarında kayboldum. – stickwithjosh