.. _`form helpers`: ================== {% uni_form %} tag ================== django-uni-form implements a class called ``FormHelper`` that defines the form rendering behavior. Helpers give you a way to control form attributes and its layout, doing this in a programatic way using Python. This way you touch HTML as little as possible, and all your logic stays in the forms and views files. For using this you will need to use django-uni-form ``{% uni_form %}`` tag. Fundamentals ~~~~~~~~~~~~ From now onwards we will use the following form to exemplify how to use a helper. This form is in charge of gathering some user information:: class ExampleForm(forms.Form): like_website = forms.TypedChoiceField( label = "Do you like this website?", choices = ((1, "Yes"), (0, "No")), coerce = lambda x: bool(int(x)), widget = forms.RadioSelect, initial = '1', required = True, ) favorite_food = forms.CharField( label = "What is you favorite food?", max_length = 80, required = True, ) favorite_color = forms.CharField( label = "What is you favorite color?", max_length = 80, required = True, ) favorite_number = forms.IntegerField( label = "Favorite number", required = False, ) notes = forms.CharField( label = "Additional notes or feedback", required = False, ) Let's see how helpers works step by step, with some examples explained. First you will need to import ``FormHelper``:: from uni_form.helper import FormHelper Your helper can be a class level variable or an instance level variable, if you don't know what this means you might want to read the article "`Be careful how you use static variables in forms`_". As a rule of thumb, if you are not going to manipulate a form helper in your code, like in a view, you should be using a static helper, otherwise you should be using an instance level helper. If you still don't understand the subtle differences between both, use an instance level helper, because you won't end up suffering side effects. As in the next steps I will show you how to manipulate the form helper, so we will create an instance level helper. This is how you would do it:: from uni_form.helper import FormHelper class ExampleForm(forms.Form): [...] def __init__(self, *args, **kwargs): self.helper = FormHelper() return super(ExampleForm, self).__init__(*args, **kwargs) As you can see you need to override the constructor and call the base class constructor using ``super``. This helper doesn't set any form attributes, so it's useless. Let's see how to set up some basic FormHelper attributes:: from uni_form.helper import FormHelper from uni_form.layout import Submit class ExampleForm(forms.Form): [...] def __init__(self, *args, **kwargs): self.helper = FormHelper() self.helper.form_id = 'id-exampleForm' self.helper.form_class = 'blueForms' self.helper.form_method = 'post' self.helper.form_action = 'submit_survey' self.helper.add_input(Submit('submit', 'Submit') return super(ExampleForm, self).__init__(*args, **kwargs) Note that we are importing here a class called ``Submit`` that is a layout object. We will see what layout objects are in detail later on, for now on let's just say that this adds a submit button to our form, so people can send their survey. We've also done some helper magic. ``FormHelper`` has a list of attributes that can be set, that affect mainly form attributes. Our form will have as DOM id ``id-exampleForm``, it will have as DOM CSS class ``blueForms``, it will use http ``POST`` to send information and its action will be set to ``reverse(submit_survey)``. Let's how you render a form with a helper using django-uni-form custom tags. First we need to load django-uni-form tags in the templates we use them: {% load uni_form_tags %} Supposing we have the form in the template context as ``example_form``, we would render it doing: {% uni_form example_form example_form.helper %} This is exactly the html that you would get::
What you'll get is the form rendered as HTML with awesome bits. Specifically... * Opening and closing form tags, with id, class, action and method set as in the helper:: * Django's CSRF controls:: * Submit button:: Manipulating a helper in a view ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Let's see how we could change any helper property in a view:: @login_required() def inbox(request, template_name): example_form = ExampleForm() redirect_url = request.GET.get('next') # Form handling logic [...] if redirect_url is not None: example_form.helper.form_action = reverse('submit_survey') + '?next=' + redirectUrl return render_to_response(template_name, {'example_form': example_form}, context_instance=RequestContext(request)) We are changing ``form_action`` helper property in case the view was called with a ``next`` GET parameter. Rendering several forms with helpers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Often we get asked, how do you render two or more forms, with their respective helpers, using ``{% uni_form %}`` tags, without having `` You can read a list of :ref:`helper attributes` and what they are for. Make django-uni-form fail loud ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default when django-uni-form encounters errors, it fails silently, logs them and continue working if possible. A settings variable called ``UNIFORM_FAIL_SILENTLY`` has been added so that you can control this behavior. If you want django-uni-form to raise errors instead of logging, telling you what’s going on when you are developing in debug mode, you can set it to:: UNIFORM_FAIL_SILENTLY = not DEBUG Rendering a formset ~~~~~~~~~~~~~~~~~~~ ``{% uni_form %}`` tag supports formsets rendering too. All the previous stated things apply to formsets the same way. Imagine you create a formset using the previous ``ExampleForm`` form:: from django.forms.models import formset_factory ExampleFormset = formset_factory(ExampleForm, extra = 3) example_formset = ExampleFormset() This is how you would render the formset:: {% uni_form formset formset.form.helper %} Note that you can still use a helper (in this case we are using the helper of the form used for building the formset). The main difference here is that helper attributes are applied to the form structure, while the layout is applied to the formset’s forms. Rendering formsets injects some extra context in the layout rendering so that you can do things like:: HTML("{% if forloop.first %}Message displayed only in the first form of a formset forms list{% endif %}", Fielset("Item {{ forloop.counter }}", 'field-1', [...]) Basically you can access a ``forloop`` Django node, as if you were rendering your formsets forms using a for loop. .. _`helper attributes`: Helper attributes you can set ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ form_method Specifies form method attribute. You can see it to ‘POST’ or ‘GET’. Defaults to ‘POST’ form_action Applied to the form action attribute. Can be a named url in your URLconf that can be executed via the {% url %} template tag. Example: ‘show_my_profile’. In your URLconf you could have something like:: url(r'^show/profile/$', 'show_my_profile_view', name = 'show_my_profile') You can also point it to a URL ‘/whatever/blabla/’. form_id Specifies form DOM id attribute. If no id provided then no id attribute is created on the form. form_class String containing separated CSS clases to be applied to form class attribute. The form will always have by default ‘uniForm’ class. form_tag It specifies if ```` tags should be rendered when using a Layout. If set to False it renders the form without the ```` tags. Defaults to True. form_error_title If you are rendering a form using {% uni_form %} tag and it has non_field_errors to display, they are rendered in a div. You can set the title of the div with this attribute. Example: “Form Errors”. formset_error_title If you are rendering a formset using {% uni_form %} tag and it has non_form_errors to display, they are rendered in a div. You can set the title of the div with this attribute. Example: “Formset Errors”. form_style If you are using uni-form CSS, it has two different form styles built-in. You can choose which one to use, setting this variable to “default” or “inline”. ======= Layouts ======= Fundamentals ~~~~~~~~~~~~ You might be thinking that helpers are nice, but what if you need to change the way the form fields are rendered, answer is layouts. Django-uni-form defines another powerful class called ``Layout``. You can create your ``Layout`` to define how the form fields should be rendered: order of the fields, wrap them in divs or other structures, add html, set ids or classes to whatever you want, etc. And all that without writing a custom template, rather fully reusable without writing it twice. Then just attach the layout to a helper, layouts are optional, but probably the most powerful thing django-uni-form has to offer. A Layout is constructed by layout objects, which can be thought of as form components. You assemble your layout using those. For the time being, your choices are: ``ButtonHolder``, ``Button``, ``Div``, ``Row``, ``Column``, ``Fieldset``, ``HTML``, ``Hidden``, ``MultiField``, ``Reset`` and ``Submit``. All these components are explained later in :ref:`layout objects`. What you need to know now about them is that every component renders a different template. Let’s write a couple of different layouts for our form, continuing with our form class example (note that the full form is not shown again): Let's add a layout to our helper:: from uni_form.helper import FormHelper from uni_form.layout import Layout, Fieldset class ExampleForm(forms.Form): [...] def __init__(self, *args, **kwargs): self.helper = FormHelper() self.helper.layout = Layout( Fieldset( 'first arg is the legend of the fieldset', 'like_website', 'favorite_number', 'favorite_color', 'favorite_food', 'notes' ) ButtonHolder( Submit('submit', 'Submit', css_class='button white') ) ) return super(ExampleForm, self).__init__(*args, **kwargs) When we render the form now using:: {% load uni_form_tags %} {% uni_form example_form example_form.helper %} We will get the fields wrapped in a fieldset, whose legend will be set to 'first arg is the legend of the fieldset'. The fields order will be: ``like_website``, ``favorite_number``, ``favorite_color``, ``favorite_food`` and ``notes``. We also get a submit button wrapped in a ``