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

    У форм в 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)

    Решение получилось простое, не захламляющее код и работоспособное.

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 25

      0
      Вещь простая, но все равно спасибо!
      Поправьте ссылку, пожалуйста: docs.djangoproject.com/en/dev/topics/forms/index/#customizing-the-form-template
        0
        Поправил, спасибо.
        +1
        > Если форм несколько, то
        выносим код в отдельный шаблон form.html, и {% include %} его по необходимости.

          +2
          form.html
          {{ form.non_field_errors }}
          {% for field in form %}
          	<div class="f_row">
          		{{ field.errors }}
          		{{ field.label_tag }}
          		<div class="f_input">
          			{{ field }}
          			{% if field.help_text %}<div class="note">{{ field.help_text }}</div>{% endif %}
          		</div>
          	</div>
          {% endfor %}
          


          other_template.html
          {% with add_post_form as form %}{% include 'form.html' %}{% endwith %}
            +1
            Да, этот вариант я рассматривал. Лично мне он не подошел, т.к. не решает задачу присвоения полям css-классов, а верстка требовала именно этого.
            +1
            Верстальщики за это спасибо скажут. Самому приходится верстать проекты под джанго и формы вызывают особое недовольство. Очень неудобно, когда приходится дергать программиста, чтобы что-то изменить в форме.
              0
              Возможно, хотя мне привычнее, чтобы вгенший вид форм из css-а трогали. Для всяких сложных полей проще один раз написать свой виджет.
            0
            По моему осталось проманкипатчить кое что, чтоб этот метод стал доступным… Или каким образом вы передаёте именно этот сабкласс форме?
              +1
              у меня например вот так

              def as_div(self):
                  return self._html_output(
                      normal_row = u'<div class="js-active forms__width %(html_class_attr)s" >\
                                          <div class="forms__i">\
                                              %(label)s %(field)s\
                                          </div>\
                                          %(help_text)s\
                                          <div class="forms__hint">%(errors)s</div></div>',
                      error_row = u'%s',
                      row_ender = u'',
                      help_text_html = u'<div class="forms__hint">%s</div>',
                      errors_on_separate_row = False
                      )
              
              
              forms.BaseForm.as_div = as_div
              

              обратите внимание на последнею строчку
                0
                Мне самым простым и прямым решением показалось просто наследоватьс вои формы не от forms.Form, а от своего класса. Кроме того, так решается задача централизованной раздачи css-классов.
                  0
                  Это конечно же хорошо, но как вы будете отображать формы из 3rd party библиотек? Всё равно манкипатчить придётся… Мне кажется лучше сразу так и поступить. И цсс классы можно точно так же проманкипатчить, ничего в этом плохого не вижу.

                  В любом случае, каждый пусть решает как ему удобней это сделать, я на своём мнении не настаиваю.
                    0
                    Да, логично.
                +2
                self.fields[field] выглядит как-то плохо.

                for field in self.fields.values():
                            field.widget.attrs['class'] = 'some-class other-class'
                
                  –7
                  ну и говнокод…
                    –2
                    Вот так шаг за шагом вы и придете к Друпалу :)

                    >>as_p(), as_table() и as_ul()
                    а где as_div и as_fieldset ??? джангисты не знают, что значит перечислиый список и параграф, что эти методы там вообще делают?
                      0
                      наиболее употребимое вынесено в отдельные методы, никто не мешает сделать и as_div, и as_fieldset, и руками нарисовать форму.
                        –13
                        угу, ничто не мешает выкинуть джангу нафиг и написать нормальный движок ;-)
                          +4
                          нет, проще трепать об этом языком на хабре.
                            –11
                            не суди всех по себе, ленивый бездарь.
                              0
                              ok
                        +1
                        Неа, не придем) Добавили 3 метода 4 года назад назад, убирать потом не стали — есть они не просят, часто удобны, обратная совместимость. А новые очень вряд ли появятся, т.к. добавишь as_div — попросят as_fieldset и тд, и будет друпал. Когда внешний вид или возможность настройки не очень важны, можно использовать as_* — это быстро и просто, когда это важно — делать по-другому, это тоже не сложно, никто эти as_* не навязывает.
                        0
                        сорри, предыдущий комент адресован не вам, а в главную ветку.
                        0
                        еще django-uni-form хвалят, сам не пробовал, правда
                          +1
                          Для required-поля можно потом вставлять звездочку, например, так:

                          $("form ul li.field-required label").append(' *');

                          Ну это так, чтобы не переписывать шаблон.
                          • UFO just landed and posted this here

                          Only users with full accounts can post comments. Log in, please.