*****
Forms
*****
"Django provides a range of tools and libraries to help you build forms to
accept input from site visitors, and then process and respond to the
input."
-- :djangodocs:`Working with forms | Django documentation `
Now that the frontend login is working, we can build the form to create
bookmarks.
Add URLs for the forms
======================
First you have to extend the marcador app's URLconf in
:file:`mysite/marcador/urls.py` by two new URLs:
.. literalinclude:: ../src/mysite/marcador/urls.py
:linenos:
:emphasize-lines: 7-10
The first one ``^create/$`` is completely static and binds the
corresponding view to the path ``/create/``.
.. index:: Primary key
The pattern ``^edit/(?\d+)/$`` has a variable part which catches
the primary key (PK) of a bookmark. The PK is added to every model by
Django, so every entry gets a unique identifier. The path ``/edit/1/``
for example leads to the bookmark with the PK ``1``.
Between the static parts ``edit/`` at the beginning and ``/`` at the end
stands the group ``(?P\d+)``. With ``\d+`` it catches any number
of numerical characters and stores them in the variable ``pk`` which can
be used in the view.
.. index:: Form, ModelForm
Add the Form
============
The next step is the form. To create it, add the file
:file:`mysite/marcador/forms.py`:
.. literalinclude:: ../src/mysite/marcador/forms.py
:linenos:
The form class ``BookmarkForm`` is also called a :djangodocs:`ModelForm
` because it inherits from
``django.forms.ModelForm``. ModelForms automatically create form fields
for every field in the model to which they belong. The linking of the
models is done in the inner class ``Meta`` which must be present in
every ModelForm.
For security reasons the value ``owner`` mustn't be editable through the form
because otherwise it would be possible to foist a bookmark on another user.
Therefore ``owner`` is added to the list of excluded fields. The same is done
with the fields ``date_created`` and ``date_updated`` because they are
:ref:`set automatically `.
Add the Views
=============
Now you can create two new views which make use of the ``BookmarkForm``.
The first one is used to create new bookmarks:
.. literalinclude:: ../src/mysite/marcador/views.py
:linenos:
:lines: 1-2, 4-40
:emphasize-lines: 1, 5, 25-39
The decorator ``@login_required`` makes sure that the view is only
accessible by authenticated users. Other visitors are redirected to the
login page.
The first step is to check if the request was done via POST. If yes, the
form is initialized with the POST data.
The form gets validated by using ``form.is_valid()``. If the validation was
successful, the new bookmark can be saved with ``form.save(commit=False)``. The
argument ``commit=False`` prevents the ModelForm from saving the bookmark, it
just returns the model instance prepared with the validated data. Now the
current user is added as the owner and the bookmark is saved. After that
``form.save_m2m()`` is called to create the relationships to the ``Tag``
models. Finally the user gets redirected to his or her bookmark list.
If the validation wasn't successful or if the request was done via GET,
the form is passed to the template where it can be rendered including
eventual error messages.
Two values are passed to the template as context variables. The dictionary key
``form`` contains the instance of ``BookmarkForm``. ``create`` is a boolean
value and is used to determine if the template is used to create or edit a
bookmark because it is used in both views.
The second view allows editing of bookmarks:
.. literalinclude:: ../src/mysite/marcador/views.py
:linenos:
:emphasize-lines: 3, 43-57
Here the primary key ``pk`` is used to fetch the bookmark from the
database. If no bookmark with this PK exists, the HTTP status code 404
is returned. If the current user isn't the owner (``bookmark.owner !=
request.user``) and isn't a superuser (``not
request.user.is_superuser``), access is denied and the HTTP status code
403 is returned.
Apart from that, the process is similar to ``bookmark_create``. But the
form is initialized with a bookmark instance (``instance=bookmark``).
This way all form fields are pre-filled with the old values. If POST
data is present, it overrides the values from the bookmark instance.
In addition the key ``create`` in the context dictionary is set to ``False``
because this view is only used to edit existing bookmarks.
Add the Templates
=================
Now you can add the template for the form
:file:`mysite/marcador/templates/marcador/form.html`:
.. literalinclude:: ../src/mysite/marcador/templates/marcador/form.html
:language: html+django
:linenos:
The variable ``create`` is used in the ``title`` and ``heading`` blocks to
display different text depending on whether the template is used to create or
edit a bookmark. The URL for the form's ``action`` attribute is also set
according to the value of ``create`` and assigned to ``action_url``. Note that
the current bookmark fetched from the database has been assigned to
``form.instance`` and can be used to create the edit URL for this bookmark.
Now you can add a link to create new bookmarks to the file
:file:`mysite/templates/toggle_login.html`. The corresponding line is
highlighted:
.. literalinclude:: ../src/mysite/templates/toggle_login.html
:language: html+django
:lines: 1-14
:emphasize-lines: 3
:linenos:
The last step is to extend the template
:file:`mysite/marcador/templates/marcador/bookmark.html` at the end with the
following lines:
.. literalinclude:: ../src/mysite/marcador/templates/marcador/bookmark.html
:language: html+django
:lines: 15-22
:emphasize-lines: 4-7
:linenos:
:lineno-start: 15
This way authenticated users see an edit button bellow their own
bookmarks.
.. raw:: latex
\newpage
Test the form
=============
Now load the main page and click on the link to create a new bookmark. You
should see this form:
.. image:: /images/marcador-create-bookmark.*
:alt: Create Bookmark Form
:align: center
It displays all fields we have configured in the form. Tags can't be added, but
you can build a second form to do this.
.. raw:: latex
\newpage
If you click on one the links to edit a bookmark you will see this edit form:
.. image:: /images/marcador-edit-bookmark.*
:alt: Edit Bookmark Form
:align: center