Upgrade to Pro — share decks privately, control downloads, hide ads and more …

django-crispy-forms

 django-crispy-forms

Pygrunn 2012 Keynote given by Miguel Araujo (@maraujop) about django-crispy-forms

Miguel Araujo

May 16, 2012
Tweet

More Decks by Miguel Araujo

Other Decks in Programming

Transcript

  1. Django forms {{ example_form.as_ul }} <li> <label for="id_username">Username:</label> <input type="text"

    name="username" id="id_username"> </li> <li> <label for="id_email">Email:</label> <input type="text" name="email" id="id_email"> </li>
  2. Django forms Moving chunks of code class ExampleForm(forms.Form): email =

    forms.CharField() username = forms.CharField() •
  3. class ExampleForm(forms.Form): email = forms.CharField() username = forms.CharField() class ExampleForm(forms.Form):

    username = forms.CharField() email = forms.CharField() Django forms Moving chunks of code •
  4. Django forms Make comment the first field? class ExampleForm(forms.Form): email

    = forms.CharField() username = forms.CharField() class ExtraFieldForm(ExampleForm): comment = forms.CharField() •
  5. Django forms self.fields is a SortedDict self.fields.keyOrder is a list

    ['username', 'email', 'comment'] class ExtraFieldForm(ExampleForm): comment = forms.CharField() def __init__(self, *args, **kwargs): super(ExtraFieldForm, self).__init__(*args, **kwargs) self.fields.keyOrder = ['comment', 'email', 'username'] • •
  6. Django forms What if I have 100 fields? class ExtraFieldForm(ExampleForm):

    comment = forms.CharField() def __init__(self, *args, **kwargs): super(ExtraFieldForm, self).__init__(*args, **kwargs) self.fields.keyOrder.remove('comment') self.fields.keyOrder.insert(0, 'comment')
  7. Asteriks for required fields {% for field in form %}

    {{ field }} {% if field.field.required %}(*){% endif %} {% endfor %}
  8. Asteriks for required fields {% for field in form %}

    {{ field }} {% if field.field.required %}(*){% endif %} {% endfor %} What about field.errors ? What about form.non_field_errors ? etc. • • •
  9. django-crispy-forms Formerly known as django-uni-form, created by Daniel Greenfeld @pydanny

    in 2008 I joined the project in the middle of 2010 and became lead developer 38 contributors Tested and thoroughly used Two template packs: bootstrap & uni_form CRISPY_TEMPLATE_PACK • • • • •
  10. django-crispy-forms A filter |crispy A tag {% crispy %} They

    work on forms, modelforms and formsets • • •
  11. |crispy filter Easy div format No need to change form

    code at all {% load crispy_forms_tags %} {{ example_form|crispy }} • •
  12. Django forms {{ example_form|crispy }} <div id="div_id_username" class="clearfix control-group"> <label

    for="id_username" class="control-label requiredField">Username<span class="asteriskField">*</span></label> <div class="controls"> <input id="id_username" type="text" class="textinput textInput" name="username"> </div> </div> <div id="div_id_email" class="clearfix control-group"> <label for="id_email" class="control-label requiredField">Email<span class="asteriskField">*</span></label> [...]
  13. {% crispy %} tag I don't like writing HTML for

    forms I need customization power They need to be as DRY as possible • • •
  14. {% crispy %} tag {% crispy form [helper] %} {%

    crispy example_form %} <form class="" method="post"> <div style="display:none"><input type="hidden" name="csrfmiddlewaretoken" ...></div> <div id="div_id_username" class="clearfix control-group"> <label for="id_username" class="control-label requiredField">Username<span class="asteriskField">*</span> </label> <div class="controls"> <input id="id_username" type="text" class="textinput textInput" name="username"> </div> </div> [...] </form>
  15. {% crispy %} tag {% crispy form [helper] %} {%

    crispy example_form %} <form class="" method="post"> <div style="display:none"><input type="hidden" name="csrfmiddlewaretoken" ...></div> <div id="div_id_username" class="clearfix control-group"> <label for="id_username" class="control-label requiredField">Username<span class="asteriskField">*</span> </label> <div class="controls"> <input id="id_username" type="text" class="textinput textInput" name="username"> </div> </div> [...] </form> How do we customize this output?
  16. FormHelper attributes form_method: helper.form_method = 'post' form_action: helper.form_action = 'addItem'

    form_id form_class form_tag: helper.form_tag = False • • • • •
  17. FormHelper helpers.py Template class ExampleFormHelper(FormHelper): form_method = 'post' form_id =

    'example-form-id' {% crispy example_form example_form_helper %}
  18. forms.py class ExampleForm(forms.Form): helper = ExampleFormHelper() Template {% crispy example_form

    %} Attaching FormHelper helpers.py class ExampleFormHelper(FormHelper): layout = Layout('comment', 'username', 'email')
  19. Coupled FormHelper forms.py Template class ExampleForm(forms.Form): helper = FormHelper() helper.form_method

    = 'post' helper.form_id = 'example-form-id' {% crispy example_form %}
  20. Custom FormHelper attributes forms.py Crispy-forms Templates class ExampleForm(forms.Form): helper =

    FormHelper() helper.help_text_as_placeholder = True {{ help_text_as_placeholder }}
  21. Special attribute layout and Layout class forms.py Template class ExtraFieldForm(forms.Form):

    helper = FormHelper() helper.layout = Layout('comment', 'username', 'email') {% crispy example_form %}
  22. Layouts A bit more complex Layout Layout( Div( 'comment', 'username',

    css_id="div-wrapping-comment-and-username" ), )
  23. Layouts A bit more complex Layout Layout( Div( 'comment', 'username',

    css_id="div-wrapping-comment-and-username" ), ) Beware that layouts are rendered strict
  24. Layouts Layout power comes from layout objects: They are Python

    classes Every layout object has an associated template • •
  25. Layout objects Fieldset( "This is the legend of the fieldset",

    'comment', 'username', css_class="fieldsets", )
  26. Layout objects HTML( """<p>HTML code embedded. You can access the

    context from where form is rendered. Hi {{ user.username }}</p>""" ) HTML( "{% include 'template.html' %}" )
  27. Layout objects Setting attributes to fields In Django class ExampleForm(forms.Form):

    username = forms.CharField( widget = forms.TextInput(attrs={'class': 'whatever', 'autocomplete': 'off'}) )
  28. Layout objects Setting attributes to fields In Django In Crispy-forms

    class ExampleForm(forms.Form): username = forms.CharField( widget = forms.TextInput(attrs={'class': 'whatever', 'autocomplete': 'off'}) ) Field('username', css_class='whatever', autocomplete='off')
  29. Layout decoupled class GlobalLayout(Layout): def __init__(self, *args, **kwargs): self.fields =

    ['username', 'email'] self.fields.append(args) class LayoutChunk(Layout): def __init__(self): self.fields = [ HTML("Hero?"), Div('name') ] GlobalLayout('comment') LayoutChunk()
  30. Dynamic Layouts ! If you manipulate a helper, use an

    instance variable class ExampleForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.helper = FormHelper() self.helper.layout = Layout( 'comment', 'username' )
  31. Custom Layout object class TR(object): template = 'myTemplate.html' # <tr><td>{{

    field|safe }}</td></tr> def __init__(self, field): self.field = field def render(self, form, form_style, context): field = render_field(field, form, form_style, context) return render_to_string(self.template, Context({'field': field}))
  32. Templates Can be easily overriden at various levels There are

    layout templates and global templates • •
  33. Overriding Layout objects templates Globally Individually from crispy_forms.layout import Div

    Div.template = 'mydiv.html' Div('field_name', template='mydiv.html')
  34. field.html template crispy_forms/templates/{bootstrap|uni_form}/field.html <div id="div_{{ field.auto_id }}" class="...> <label ...>

    {% if field|css_class == 'radioselect' %} {% include 'bootstrap/layout/radioselect.html' %} {% endif %} [...] {% if field|css_class != "radioselect" and ... %} {% crispy_field field %} {% include 'bootstrap/layout/help_text_and_errors.html' %} {% endif %} </div>
  35. field.html template crispy_forms/templates/{bootstrap|uni_form}/field.html <div id="div_{{ field.auto_id }}" class="...> <label ...>

    {% if field|css_class == 'radioselect' %} {% include 'bootstrap/layout/radioselect.html' %} {% endif %} [...] {% if field|css_class != "radioselect" and ... %} {% crispy_field field %} {% include 'bootstrap/layout/help_text_and_errors.html' %} {% endif %} </div> {% crispy_field field %} != {{ field }}
  36. All selects using chosen class AnimalForm(forms.form): animal = forms.ChoiceField(choices=[ ('cat',

    'cat'), ('dog', 'dog'), ]) food = forms.ChoiceField(choices=[ ('meat', 'meat'), ('fish', 'fish'), ]) helper = FormHelper() helper.form_class = 'form-horizontal' $(document).ready(function () { $(".chzn-select").chosen(); });
  37. All selects using chosen field.html {% if field|css_class == "select"

    %} <div class="controls"> {% crispy_field field 'class' 'chzn-select' %} </div> {% endif %} {% if field|css_class != "checkboxselectmultiple" and field|css_class != "radioselect" and field|css_class != "select" %} [...]
  38. Field labels as holders .holder { color: #999; font-size: 19px;

    position: relative; left: -210px; top: 4px; }
  39. Field labels as holders field.html (Using a custom attribute) {%

    if not label_as_holder and field.label and not field|is_checkbox %} <label for="{{ field.id_for_label }}" class="control-label" ...> {{ field.label|safe }} </label> {% endif %} {% crispy_field field %} {% if label_as_holder %} <span class="holder {% if field.value %}hidden{% endif %}">{{ field.label }}</span> {% endif %}
  40. Scratching the surface There is more power, filters, layout objects,

    attributes, etc. Javascript validation Visit the docs django-crispy-forms.rtfd.org There is more coming • • • •