Django Forms, ModelChoiceField с использованием виджета RadioSelect, сгруппированы по FK - PullRequest
3 голосов
/ 18 марта 2009

Задача, вывести радио-выборку во вложенном <ul></ul>, сгруппированном по заданию fk.

т.

class Category(models.Model):
    # ...

class Task(models.Model):
    # ...
    category = models.ForeignKey(Category)
    # ...

forms.py

class ActivityForm(forms.ModelForm):
    # ...
    task = forms.ModelChoiceField(
        queryset    = Task.objects.all(),
        widget      = RadioSelectGroupedByFK
    )

widgets.py

class RadioFieldRendererGroupedByFK(RadioFieldRenderer):
    """
    An object used by RadioSelect to enable customization of radio widgets.
    """
    #def __init__(self, attrs=None):
        # Need a radio select for each?? Just an Idea.
        #widgets = (RadioSelect(attrs=attrs), RadioSelect(attrs=attrs))
        #super(RadioFieldRendererGroupedByFK, self).__init__(widgets, attrs)

    def render(self):
        """Outputs nested <ul> for this set of radio fields."""
        return mark_safe(
            #### Somehow the crux of the work happens here? but how to get the
            #### right context??
            u'<ul>\n%s\n</ul>' % u'\n'.join(
                [u'<li>%s</li>' % force_unicode(w) for w in self]
            )
        )

class RadioSelectGroupedByFK(forms.RadioSelect):
    renderer = RadioFieldRendererGroupedByFK

Лучшее спасибо !!

1 Ответ

1 голос
/ 25 марта 2009

itertools.groupby() идеально подходит для этого. Я предполагаю, что Task и Category каждый имеет атрибут name, по которому вы хотите их отсортировать. Я не знаю API Django, поэтому вы, вероятно, захотите перенести сортировку в запрос db, и вам нужно будет посмотреть атрибуты виджета, чтобы выяснить, как получить доступ к объектам задачи, но вот основная формула:

from itertools import groupby

# sort first since groupby only groups adjacent items
tasks.sort(key=lambda task: (task.category.name, task.name))

for category, category_tasks in groupby(tasks, key=lambda task: task.category):
  print '%s:' % category.name
  for task in category_tasks:
    print '* %s' % task.name

Это должно напечатать список как:

Breakfast foods:
* eggs
* spam
Dinner foods:
* spam
* spam
* spam

НТН

...