Rendering forms in templates

After creating the form, programming the view, and adding the URL pattern, we are only missing the template for this view. Create a new file in the blog/templates/blog/post/ directory and name it share.html; add the following code to it:

{% extends "blog/base.html" %}

{% block title %}Share a post{% endblock %}

{% block content %}
{% if sent %}
<h1>E-mail successfully sent</h1>
<p>
"{{ post.title }}" was successfully sent to {{ form.cleaned_data.to }}.
</p>
{% else %}
<h1>Share "{{ post.title }}" by e-mail</h1>
<form action="." method="post">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" value="Send e-mail">
</form>
{% endif %}
{% endblock %}

This is the template to display the form or a success message when it's sent. As you would notice, we create the HTML form element, indicating that it has to be submitted by the POST method:

<form action="." method="post">

Then, we include the actual form instance. We tell Django to render its fields in HTML paragraph <p> elements with the as_p method. We can also render the form as an unordered list with as_ul or as an HTML table with as_table. If we want to render each field, we can also iterate through the fields, as in the following example:

{% for field in form %}
<p>
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</p>
{% endfor %}

The {% csrf_token %} template tag introduces a hidden field with an autogenerated token to avoid cross-site request forgery (CSRF) attacks. These attacks consist of a malicious website or program performing an unwanted action for a user on your site. You can find more information about this at https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF).

The preceding tag generates a hidden field that looks like this:

<input type='hidden' name='csrfmiddlewaretoken' value='26JjKo2lcEtYkGoV9z4XmJIEHLXN5LDR' />

By default, Django checks for the CSRF token in all POST requests. Remember that you include the csrf_token tag in all forms that are submitted via POST.

Edit your blog/post/detail.html template and add the following link to the share post URL after the {{ post.body|linebreaks }} variable:

<p>
<a href="{% url "blog:post_share" post.id %}">
Share this post
</a>
</p>

Remember that we are building the URL dynamically using the {% url %} template tag provided by Django. We are using the namespace called blog and the URL named post_share, and we are passing the post ID as a parameter to build the absolute URL.

Now, start the development server with the python manage.py runserver command and open http://127.0.0.1:8000/blog/ in your browser. Click on any post title to view its detail page. Under the post body, you should see the link we just added, as shown in the following screenshot:

Click on Share this post, and you should see the page including the form to share this post by email, as follows:

CSS styles for the form are included in the example code in the static/css/blog.css file. When you click on the SEND E-MAIL button, the form is submitted and validated. If all fields contain valid data, you will get a success message, as follows:

If you input invalid data, you will see that the form is rendered again, including all validation errors:

Note that some modern browsers will prevent you from submitting the form with empty or erroneous fields. This is because of form validation done by the browser based on field types and restrictions per field. In this case, the form won't be submitted and the browser will display an error message for the fields that are wrong.

Our form for sharing posts by email is now complete. Let's create a comment system for our blog.