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

1b6429aafe9e31ea3e3586e2edf80211?s=128

Miguel Araujo

May 16, 2012
Tweet

Transcript

  1. django-crispy-forms Miguel Araujo @maraujop

  2. Django forms

  3. Django forms form.as_ul form.as_p form.as_table • • •

  4. Django forms class ExampleForm(forms.Form): username = forms.CharField() email = forms.CharField()

  5. 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>
  6. How about divs?

  7. Reorder fields?

  8. Django forms Moving chunks of code class ExampleForm(forms.Form): email =

    forms.CharField() username = forms.CharField() •
  9. 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 •
  10. Django forms Make comment the first field? class ExampleForm(forms.Form): email

    = forms.CharField() username = forms.CharField() class ExtraFieldForm(ExampleForm): comment = forms.CharField() •
  11. 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'] • •
  12. 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')
  13. Django ModelForms ModelForms are different, why? class ExampleForm(forms.ModelForm): class Meta:

    model = ExampleModel fields = ('username', 'email') •
  14. Customize output?

  15. Asteriks for required fields {% for field in form %}

    {{ field }} {% if field.field.required %}(*){% endif %} {% endfor %}
  16. 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. • • •
  17. Something more complex?

  18. None
  19. 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 • • • • •
  20. django-crispy-forms A filter |crispy A tag {% crispy %} They

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

    code at all {% load crispy_forms_tags %} {{ example_form|crispy }} • •
  22. 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> [...]
  23. None
  24. {% crispy %} tag I don't like writing HTML for

    forms I need customization power They need to be as DRY as possible • • •
  25. {% 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>
  26. {% 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?
  27. FormHelper They control global form rendering behaviour They are form

    decoupled • •
  28. FormHelper attributes form_method: helper.form_method = 'post' form_action: helper.form_action = 'addItem'

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

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

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

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

    FormHelper() helper.help_text_as_placeholder = True {{ help_text_as_placeholder }}
  33. Programmatic layouts

  34. Special attribute layout and Layout class forms.py Template class ExtraFieldForm(forms.Form):

    helper = FormHelper() helper.layout = Layout('comment', 'username', 'email') {% crispy example_form %}
  35. Layout( 'comment', 'email', 'username', ) Layouts Basic Layout for ExtraFieldForm

  36. Layouts Custom output is defined by a Python layout Flexible

    and highly reusable • •
  37. Layouts A bit more complex Layout Layout( Div( 'comment', 'username',

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

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

    classes Every layout object has an associated template • •
  40. Layout objects Div( 'comment', 'username', css_id="div-wrapping-fields", css_class="crispy-divs", data-markup="crispy rocks" )

  41. Layout objects Fieldset( "This is the legend of the fieldset",

    'comment', 'username', css_class="fieldsets", )
  42. 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' %}" )
  43. Layout objects Submit('name', 'value') FormActions( Submit('save_changes', 'Save changes', css_class="btn-primary"), Submit('cancel',

    'Cancel'), )
  44. Layout objects Setting attributes to fields In Django class ExampleForm(forms.Form):

    username = forms.CharField( widget = forms.TextInput(attrs={'class': 'whatever', 'autocomplete': 'off'}) )
  45. 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')
  46. Nested Layout objects Div( Div( 'name', 'value' ), HTML("Hero?") )

  47. None
  48. Crispy-forms internals Layout has a fields attribute All layout objects

    have a fields attribute • •
  49. 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()
  50. Layout composition class ComplexForm(forms.ModelForm): helper = FormHelper() helper.layout = Layout(

    GlobalLayout(), 'extra_field' )
  51. Dynamic Layouts views.py def view(request): form = ExampleForm() form.helper.layout.fields.append(HTML("<p>Added extra

    HTML</p>")) [...] return render(request, 'template.html', {'form': form})
  52. 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' )
  53. None
  54. Custom Layout object TR('username') <tr><td>{{ field }}</td></tr>

  55. 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}))
  56. Examples

  57. None
  58. None
  59. Crispy-forms templates

  60. Templates Can be easily overriden at various levels There are

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

    Div.template = 'mydiv.html' Div('field_name', template='mydiv.html')
  62. 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>
  63. 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 }}
  64. Examples

  65. 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(); });
  66. 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" %} [...]
  67. Field labels as holders .holder { color: #999; font-size: 19px;

    position: relative; left: -210px; top: 4px; }
  68. 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 %}
  69. 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 • • • •
  70. Thanks, questions? @maraujop