Slide 1

Slide 1 text

django-crispy-forms Miguel Araujo @maraujop

Slide 2

Slide 2 text

Django forms

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Django forms {{ example_form.as_ul }}
  • Username:
  • Email:
  • Slide 6

    Slide 6 text

    How about divs?

    Slide 7

    Slide 7 text

    Reorder fields?

    Slide 8

    Slide 8 text

    Django forms Moving chunks of code class ExampleForm(forms.Form): email = forms.CharField() username = forms.CharField() •

    Slide 9

    Slide 9 text

    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 •

    Slide 10

    Slide 10 text

    Django forms Make comment the first field? class ExampleForm(forms.Form): email = forms.CharField() username = forms.CharField() class ExtraFieldForm(ExampleForm): comment = forms.CharField() •

    Slide 11

    Slide 11 text

    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'] • •

    Slide 12

    Slide 12 text

    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')

    Slide 13

    Slide 13 text

    Django ModelForms ModelForms are different, why? class ExampleForm(forms.ModelForm): class Meta: model = ExampleModel fields = ('username', 'email') •

    Slide 14

    Slide 14 text

    Customize output?

    Slide 15

    Slide 15 text

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

    Slide 16

    Slide 16 text

    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. • • •

    Slide 17

    Slide 17 text

    Something more complex?

    Slide 18

    Slide 18 text

    No content

    Slide 19

    Slide 19 text

    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 • • • • •

    Slide 20

    Slide 20 text

    django-crispy-forms A filter |crispy A tag {% crispy %} They work on forms, modelforms and formsets • • •

    Slide 21

    Slide 21 text

    |crispy filter Easy div format No need to change form code at all {% load crispy_forms_tags %} {{ example_form|crispy }} • •

    Slide 22

    Slide 22 text

    Django forms {{ example_form|crispy }}
    Username*
    Email* [...]

    Slide 23

    Slide 23 text

    No content

    Slide 24

    Slide 24 text

    {% crispy %} tag I don't like writing HTML for forms I need customization power They need to be as DRY as possible • • •

    Slide 25

    Slide 25 text

    {% crispy %} tag {% crispy form [helper] %} {% crispy example_form %}
    Username*
    [...]

    Slide 26

    Slide 26 text

    {% crispy %} tag {% crispy form [helper] %} {% crispy example_form %}
    Username*
    [...] How do we customize this output?

    Slide 27

    Slide 27 text

    FormHelper They control global form rendering behaviour They are form decoupled • •

    Slide 28

    Slide 28 text

    FormHelper attributes form_method: helper.form_method = 'post' form_action: helper.form_action = 'addItem' form_id form_class form_tag: helper.form_tag = False • • • • •

    Slide 29

    Slide 29 text

    FormHelper helpers.py Template class ExampleFormHelper(FormHelper): form_method = 'post' form_id = 'example-form-id' {% crispy example_form example_form_helper %}

    Slide 30

    Slide 30 text

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

    Slide 31

    Slide 31 text

    Coupled FormHelper forms.py Template class ExampleForm(forms.Form): helper = FormHelper() helper.form_method = 'post' helper.form_id = 'example-form-id' {% crispy example_form %}

    Slide 32

    Slide 32 text

    Custom FormHelper attributes forms.py Crispy-forms Templates class ExampleForm(forms.Form): helper = FormHelper() helper.help_text_as_placeholder = True {{ help_text_as_placeholder }}

    Slide 33

    Slide 33 text

    Programmatic layouts

    Slide 34

    Slide 34 text

    Special attribute layout and Layout class forms.py Template class ExtraFieldForm(forms.Form): helper = FormHelper() helper.layout = Layout('comment', 'username', 'email') {% crispy example_form %}

    Slide 35

    Slide 35 text

    Layout( 'comment', 'email', 'username', ) Layouts Basic Layout for ExtraFieldForm

    Slide 36

    Slide 36 text

    Layouts Custom output is defined by a Python layout Flexible and highly reusable • •

    Slide 37

    Slide 37 text

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

    Slide 38

    Slide 38 text

    Layouts A bit more complex Layout Layout( Div( 'comment', 'username', css_id="div-wrapping-comment-and-username" ), ) Beware that layouts are rendered strict

    Slide 39

    Slide 39 text

    Layouts Layout power comes from layout objects: They are Python classes Every layout object has an associated template • •

    Slide 40

    Slide 40 text

    Layout objects Div( 'comment', 'username', css_id="div-wrapping-fields", css_class="crispy-divs", data-markup="crispy rocks" )

    Slide 41

    Slide 41 text

    Layout objects Fieldset( "This is the legend of the fieldset", 'comment', 'username', css_class="fieldsets", )

    Slide 42

    Slide 42 text

    Layout objects HTML( """

    HTML code embedded. You can access the context from where form is rendered. Hi {{ user.username }}

    """ ) HTML( "{% include 'template.html' %}" )

    Slide 43

    Slide 43 text

    Layout objects Submit('name', 'value') FormActions( Submit('save_changes', 'Save changes', css_class="btn-primary"), Submit('cancel', 'Cancel'), )

    Slide 44

    Slide 44 text

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

    Slide 45

    Slide 45 text

    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')

    Slide 46

    Slide 46 text

    Nested Layout objects Div( Div( 'name', 'value' ), HTML("Hero?") )

    Slide 47

    Slide 47 text

    No content

    Slide 48

    Slide 48 text

    Crispy-forms internals Layout has a fields attribute All layout objects have a fields attribute • •

    Slide 49

    Slide 49 text

    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()

    Slide 50

    Slide 50 text

    Layout composition class ComplexForm(forms.ModelForm): helper = FormHelper() helper.layout = Layout( GlobalLayout(), 'extra_field' )

    Slide 51

    Slide 51 text

    Dynamic Layouts views.py def view(request): form = ExampleForm() form.helper.layout.fields.append(HTML("

    Added extra HTML

    ")) [...] return render(request, 'template.html', {'form': form})

    Slide 52

    Slide 52 text

    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' )

    Slide 53

    Slide 53 text

    No content

    Slide 54

    Slide 54 text

    Custom Layout object TR('username') {{ field }}

    Slide 55

    Slide 55 text

    Custom Layout object class TR(object): template = 'myTemplate.html' # {{ field|safe }} 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}))

    Slide 56

    Slide 56 text

    Examples

    Slide 57

    Slide 57 text

    No content

    Slide 58

    Slide 58 text

    No content

    Slide 59

    Slide 59 text

    Crispy-forms templates

    Slide 60

    Slide 60 text

    Templates Can be easily overriden at various levels There are layout templates and global templates • •

    Slide 61

    Slide 61 text

    Overriding Layout objects templates Globally Individually from crispy_forms.layout import Div Div.template = 'mydiv.html' Div('field_name', template='mydiv.html')

    Slide 62

    Slide 62 text

    field.html template crispy_forms/templates/{bootstrap|uni_form}/field.html

    Slide 63

    Slide 63 text

    field.html template crispy_forms/templates/{bootstrap|uni_form}/field.html
    {% crispy_field field %} != {{ field }}

    Slide 64

    Slide 64 text

    Examples

    Slide 65

    Slide 65 text

    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(); });

    Slide 66

    Slide 66 text

    All selects using chosen field.html {% if field|css_class == "select" %}
    {% crispy_field field 'class' 'chzn-select' %}
    {% endif %} {% if field|css_class != "checkboxselectmultiple" and field|css_class != "radioselect" and field|css_class != "select" %} [...]

    Slide 67

    Slide 67 text

    Field labels as holders .holder { color: #999; font-size: 19px; position: relative; left: -210px; top: 4px; }

    Slide 68

    Slide 68 text

    Field labels as holders field.html (Using a custom attribute) {% if not label_as_holder and field.label and not field|is_checkbox %} {{ field.label|safe }} {% endif %} {% crispy_field field %} {% if label_as_holder %} {{ field.label }} {% endif %}

    Slide 69

    Slide 69 text

    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 • • • •

    Slide 70

    Slide 70 text

    Thanks, questions? @maraujop