Опции переключателя кнопок рендеринга Django с дополнительной информацией от модели - PullRequest
3 голосов
/ 06 апреля 2011

У меня есть модель местоположения и связанная форма, которая отображает поле «местоположение» в виде набора переключателей (используя набор запросов формы для отображения значений).Есть небольшое количество локаций, но они должны быть динамичными.Я хотел бы отобразить описание местоположения рядом с каждой опцией радиоблока, чтобы у пользователей было немного больше информации о местоположениях.

Притворимся, что это список переключателей, вот как я бы хотел, чтобы он выглядел:

<> Восток - это местоположение на восток.<> Запад - это западное место!<> Север - это северное местоположение

У меня есть модель, подобная следующей:

class Location(models.Models):
    location = models.CharField(max_length=50)
    description = models.TextField(blank=True, null=True)

И форма как таковая:

class LocationForm(forms.Form):
    location = ExtraModelChoiceField(
               widget=RadioSelect(renderer=ExtraHorizRadioRenderer), 
               queryset = models.Locations.objects.filter(active=True))

Я могуКажется, я не нашел хорошего способа визуализации формы, чтобы я мог отобразить описание вместе с каждым параметром выбора.Я сделал много переопределений, но мне не слишком повезло.

МОЯ ПОПЫТКА РЕШЕНИЯ (НО УДАЧИ ЕЩЕ):

Исходя из того, что я понял, обычно, если в поле формы предоставляется набор запросов, логика формы Django преобразует это в выборочный наборtupals.Каждый «subtupal» содержит идентификатор и метку, которые отображаются при визуализации.Я пытаюсь добавить третье значение к тем "подзаголовкам", которое будет описанием.

Я определил пользовательский рендерер для отображения моих переключателей по горизонтали и передачи своих пользовательских настроек.

class ExtraHorizRadioRenderer(forms.RadioSelect.renderer):
    def render(self):
        return mark_safe(u'\n'.join([u'%s\n' % w for w in self]))

    def __iter__(self):
        for i, choice in enumerate(self.choices):
            yield ExtraRadioInput(self.name, self.value, 
                                  self.attrs.copy(), choice, i)

    def __getitem__(self, idx):
        choice = self.choices[idx] # Let the IndexError propogate
        return ExtraRadioInput(self.name, self.value, 
                               self.attrs.copy(), choice, idx)

Я переопределил класс RadioJput Django, поэтому я могу добавить информацию описания, которую мне нужно отобразить рядом с кнопками Radio.

class ExtraRadioInput(forms.widgets.RadioInput):

    def __init__(self, name, value, attrs, choice, index):
        self.name, self.value = name, value
        self.attrs = attrs
        self.choice_value = force_unicode(choice[0])
        self.choice_label = force_unicode(choice[1])
        self.choice_description = force_unicode(choice[2])   # <--- MY ADDITION; FAILS
        self.index = index

    def __unicode__(self):
        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 tag(self):
        if 'id' in self.attrs:
            self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index)
        final_attrs = dict(self.attrs, type='radio', name=self.name, 
                      value=self.choice_value)
        if self.is_checked():
            final_attrs['checked'] = 'checked'
        return mark_safe(
           u'<input%s /><span class="description">%s</span>' % \
           (flatatt(final_attrs),self.choice_description ))  # <--- MY ADDTIONS

Я также переопределил следующие два класса Django в надежде обойти мои модифицированные варианты выбора.

class ExtraModelChoiceIterator(forms.models.ModelChoiceIterator  ):    

    def choice(self, obj): 
        if self.field.to_field_name:
            key = obj.serializable_value(self.field.to_field_name)
        else:
            key = obj.pk

        if obj.description:   # <-- MY ADDITIONS
            description = obj.description
        else:
            description = ""
        return (key, self.field.label_from_instance(obj),description)


class ExtraModelChoiceField(forms.models.ModelChoiceField):

    def _get_choices(self):
        if hasattr(self, '_choices'):
            return self._choices
        return ExtraModelChoiceIterator(self)  # <-- Uses MY NEW ITERATOR

Используя описанный выше подход, я не могу обойти свой трехзначный тупал.Я получаю ошибку «индекс кортежа вне диапазона» (вверх, где я отмечаю FAILURE выше), указывающий, что каким-то образом у моего тупала нет дополнительного значения.

Кто-нибудь видит недостаток в моей логике или, в более общем случае, есть подход к отображению описания рядом со списком вариантов с помощью виджета?

Спасибо за чтение.Любые комментарии очень ценятся.Джо

Ответы [ 2 ]

2 голосов
/ 06 апреля 2011

Извините, что отвечаю на мой вопрос, но я думаю, что у меня есть способ сделать это.Как всегда, это кажется проще, чем я делал это раньше.Переопределение метода label_from_instance в расширенном ModelChoiceField позволяет мне получить доступ к экземпляру объекта модели, чтобы иметь возможность распечатать дополнительную информацию.

from django.utils.encoding import smart_unicode, force_unicode

class ExtraModelChoiceField(forms.models.ModelChoiceField):

    def label_from_instance(self, obj):
        return mark_safe(
            "<span>%s</span><span class=\"desc\" id=\"desc_%s\">%s</span>" % (
            mart_unicode(obj), obj.id, smart_unicode(obj.description),))


class HorizRadioRenderer(forms.RadioSelect.renderer):
    # for displaying select options horizontally. 
    # https://wikis.utexas.edu/display/~bm6432/Django-Modifying+RadioSelect+Widget+to+have+horizontal+buttons
    def render(self):
        return mark_safe(u'\n'.join([u'%s\n' % w for w in self]))


class LocationForm(forms.Form):
    location = ExtraModelChoiceField(widget=forms.RadioSelect(renderer=HorizRadioRenderer),
        queryset=models.Location.objects.filter(active=True))

Если вы знаете о лучшем подходе, я был бы рад увидеть его.В противном случае это придется делать.Спасибо за прочтение.Надеюсь, это спасет кого-то от разочарования.

Джо

2 голосов
/ 06 апреля 2011

Вы смотрели на этот фрагмент: RadioSelectWithHelpText ?

...