Pull to refresh

Оформляем формы

Reading time3 min
Views20K
У форм в Django есть несколько предопределенных методов as_p(), as_table() и as_ul(), которые отображают форму как набор параграфов, таблицу или список. На практике этих методов не всегда достаточно, чтобы формы выглядели так, как нам хочется.

Допустим, верстальщик сверстал все формы div-ами. Документация предлагает в этом случае писать нужные теги в ручную в шаблоне. Если форма у нас только одна, можно поступить и так. Если форм несколько, то этот подход сразу же теряет все свое очарование. Решение очень простое — пронаследуем наш собственный класс форм от forms.Form или forms.ModelForm и обучим его рисовать формы так, как нужно нам.

Для начала неплохо заглянуть в исходники Django – методы as_p и ему подобные устроены очень просто. Они вызывают метод _html_output класса BaseForm инструктируя его, как именно рисовать форму. Итак, добавим свой метод as_div:
Copy Source | Copy HTML
  1. class SexyModelForm(forms.ModelForm):
  2.     def as_div(self):
  3.         return self._html_output(
  4.             normal_row = u'<div%(html_class_attr)s>%(label)s %(field)s %(help_text)s %(errors)s</div>',
  5.             error_row = u'<div class="error">%s</div>',
  6.             row_ender = '</div>',
  7.             help_text_html = u'<div class="hefp-text">%s</div>',
  8.             errors_on_separate_row = False)

Код самоочевиден, единственное, что может вызвать вопросы — это errors_on_separate_row. Если этот параметр установлен в True, то ошибки будут выводиться отдельным блоком. Он используется, например в as_p, чтобы не запихивать <ul> внутрь <p>.

Теперь, мы можем наследовать наши формы от SexyModelForm и вызывать их в шаблонах с помощью {{ form.as_div }}.

В 1.2. появилась приятная возможность назначать свои css-классы для обязательного поля и поля с ошибкой. Еще немного упростим себе жизнь — допишем пару строк в наш класс, и в формы добавится еще немного единообразия:
Copy Source | Copy HTML
  1. error_css_class = 'class-error'
  2. required_css_class = 'class-required'

Но и это еще не все. Бывает нужно добавить всем полям какие-то css-классы. Это можно сделать вот так:
Copy Source | Copy HTML
  1. def __init__(self, *args, **kwargs):
  2.     super(ModelForm, self).__init__(*args, **kwargs)
  3.         # adding css classes to widgets without define the fields:
  4.         for field in self.fields:
  5.             self.fields[field].widget.attrs['class'] = 'some-class other-class'

Здесь-же можно, например, проверять input_type и присваивать классы разным типам полей в зависимости от него.

Вот, что получилось в итоге:
Copy Source | Copy HTML
  1. class SexyModelForm(forms.ModelForm):
  2.     error_css_class = 'class-error'
  3.     required_css_class = 'class-required'
  4.     def __init__(self, *args, **kwargs):
  5.         super(ModelForm, self).__init__(*args, **kwargs)
  6.         # adding css classes to widgets without define the fields:
  7.         for field in self.fields:
  8.             self.fields[field].widget.attrs['class'] = 'some-class other-class'
  9.     def as_div(self):
  10.         return self._html_output(
  11.             normal_row = u'<div%(html_class_attr)s>%(label)s %(field)s %(help_text)s %(errors)s</div>',
  12.             error_row = u'<div class="error">%s</div>',
  13.             row_ender = '</div>',
  14.             help_text_html = u'<div class="hefp-text">%s</div>',
  15.             errors_on_separate_row = False)

Решение получилось простое, не захламляющее код и работоспособное.
Tags:
Hubs:
Total votes 51: ↑41 and ↓10+31
Comments25

Articles