Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting Started Documentation #986

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/concepts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Concepts
Form Helpers
-------------

The biggest advantage of this library are :ref:`form helpers` and layouts. The advantage of these tools is that they let you build forms with most of the coding done in Python, rather than HTML. We **strongly** suggest you study and learn the examples in the :ref:`form helpers` documentation.
The biggest advantage of this library are :ref:`form helper` and layouts. The advantage of these tools is that they let you build forms with most of the coding done in Python, rather than HTML. We **strongly** suggest you study and learn the examples in the :ref:`form helper` documentation.

Don't Repeat Yourself
---------------------
Expand Down
22 changes: 0 additions & 22 deletions docs/filters.rst

This file was deleted.

198 changes: 193 additions & 5 deletions docs/form_helper.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,198 @@
.. _`form helpers`:
.. _`Form Helper`:

==========
FormHelper
==========
===========
Form Helper
===========

The getting started section gives an overview of the helper. This section explains more detailed features of the helper including the various attributes that can be set to help configure your form.

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 ``{% crispy %}`` tag, without having ``<form>`` tags rendered twice?" Easy, you need to set ``form_tag`` helper property to ``False`` in every helper::

self.helper.form_tag = False

Then you will have to write a little of html code surrounding the forms::

<form action="{% url 'submit_survey' %}" class="uniForm" method="post">
{% crispy first_form %}
{% crispy second_form %}
</form>

You can read a list of :ref:`helper attributes` and what they are for.


Change '*' required fields
~~~~~~~~~~~~~~~~~~~~~~~~~~

If you don't like the use of ``*`` (asterisk) to denote required fields you have two options:

* Asterisks have an ``asteriskField`` class set. So you can hide it using CSS rule::

.asteriskField {
display: none;
}

* Override ``field.html`` template with a custom one.


Make crispy-forms fail loud
~~~~~~~~~~~~~~~~~~~~~~~~~~~

By default when crispy-forms encounters errors, it fails silently, logs them and continues working if possible. A settings variable called ``CRISPY_FAIL_SILENTLY`` has been added so that you can control this behavior. If you want to raise exceptions instead of logging, telling you what’s going on when you are developing in debug mode, you can set it to::

CRISPY_FAIL_SILENTLY = not DEBUG


Change crispy-forms <input> default classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Django fields generate default classes, crispy-forms handles these and adds other classes for compatibility with CSS frameworks.

For example a ``CharField`` generates an ``<input class="textinput" ...``. But in uni form we need the class to be ``textInput`` (with capital 'I'), so crispy-forms leaves it like ``<input class="textinput textInput"...``. All official template packs are handled automatically, but maybe you are integrating a new CSS framework, or your company's custom one, with crispy-forms and need to change the default conversions. For this you need to use a settings variable called ``CRISPY_CLASS_CONVERTERS``, expected to be a Python dictionary::

CRISPY_CLASS_CONVERTERS = {'textinput': "textinput inputtext"}

For example this setting would generate ``<input class"textinput inputtext" ...``. The key of the dictionary ``textinput`` is the Django's default class, the value is what you want it to be substituted with, in this case we are keeping ``textinput``.


Render a form within Python code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Sometimes, it might be useful to render a form using crispy-forms within Python code, like a Django view, for that there is a nice helper ``render_crispy_form``. The prototype of the method is ``render_crispy_form(form, helper=None, context=None)``. You can use it like this. Remember to pass your CSRF token to the helper method using the context dictionary if you want the rendered form to be able to submit.


AJAX validation recipe
~~~~~~~~~~~~~~~~~~~~~~

One easy way to validate a crispy-form through AJAX and re-render the resulting form errors if any is to set up a view, that validates the form and renders its html using ``render_crispy_form`` to finally return this html to the client AJAX request. Let's see an example.

Our server side code could be::

from crispy_forms.utils import render_crispy_form

@json_view
def save_example_form(request):
form = ExampleForm(request.POST or None)
if form.is_valid():
# You could actually save through AJAX and return a success code here
form.save()
return {'success': True}

# RequestContext ensures CSRF token is placed in newly rendered form_html
request_context = RequestContext(request)
form_html = render_crispy_form(form, context=request_context)
return {'success': False, 'form_html': form_html}

I'm using a jsonview decorator from `django-jsonview`_.

Note that in Django versions 1.8 and onwards, using ``RequestContext`` in this way will not work. Instead you can provide ``render_crispy_form`` with the necessary CSRF token with the following code::

from django.template.context_processors import csrf
ctx = {}
ctx.update(csrf(request))
form_html = render_crispy_form(form, context=ctx)

In our client side using jQuery would look like::

var example_form = '#example-form';

$.ajax({
url: "{% url 'save_example_form' %}",
type: "POST",
data: $(example_form).serialize(),
success: function(data) {
if (!(data['success'])) {
// Here we replace the form, for the
$(example_form).replaceWith(data['form_html']);
}
else {
// Here you can show the user a success message or do whatever you need
$(example_form).find('.success-message').show();
}
},
error: function () {
$(example_form).find('.error-message').show()
}
});

Obviously, you can adjust this snippets to your needs, or class based views or favorite frontend library.

.. warning ::

When replacing form html, you need to bind events using ``live`` or ``on`` jQuery method.

.. _`django-jsonview`: https://github.com/jsocol/django-jsonview

Bootstrap3 horizontal forms
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. image:: images/bootstrap3_horizontal_form.jpg
:align: center

The way you do horizontal forms in Bootstrap version 3 is setting some ``col-lg-X`` classes in labels and divs wrapping fields. This would mean a lot of hassle updating your layout objects for settings these classes, too much verbosity. Instead some ``FormHelper`` attributes have been added to help you easily achieve this. You will need to set only three attributes::

helper.form_class = 'form-horizontal'
helper.label_class = 'col-lg-2'
helper.field_class = 'col-lg-8'
helper.layout = Layout(
'email',
'password',
'remember_me',
StrictButton('Sign in', css_class='btn-default'),
)

Of course you can set your widths as you like, it doesn't have to be exactly like this.

Bootstrap3 inline forms
~~~~~~~~~~~~~~~~~~~~~~~

.. image:: images/bootstrap3_inline_form.jpg
:align: center

The way you do inline forms in Bootstrap version 3 is::

helper.form_class = 'form-inline'
helper.field_template = 'bootstrap3/layout/inline_field.html'
helper.layout = Layout(
'email',
'password',
'remember_me',
StrictButton('Sign in', css_class='btn-default'),
)

If you need to set attributes in a field, you have to use ``InlineField`` instead of ``Field``::

from crispy_forms.bootstrap import InlineField

helper.layout = Layout(
InlineField('email', readonly=True),
'password',
[...]
)

What is a ``FormHelper`` and how to use it, is thoroughly explained in a previous section :ref:`crispy tag forms`.


.. _`helper form attached`:
Expand Down