Creating custom template filters
Django has a variety of built-in template filters that allow you to modify variables in templates. These are Python functions that take one or two parameters—the value of the variable it's being applied to, and an optional argument. They return a value that can be displayed or treated by another filter. A filter looks like {{ variable|my_filter }}. Filters with an argument look like {{ variable|my_filter:"foo" }}. You can apply as many filters as you like to a variable, for example, {{ variable|filter1|filter2 }}, and each of them will be applied to the output generated by the preceding filter.
We will create a custom filter to be able to use markdown syntax in our blog posts and then convert the post contents to HTML in the templates. Markdown is a plain text formatting syntax that is very simple to use, and it's intended to be converted into HTML. You can learn the basics of this format at https://daringfireball.net/projects/markdown/basics.
First, install the Python markdown module via pip using the following command:
pip install Markdown==2.6.11
Then, edit the blog_tags.py file and include the following code:
from django.utils.safestring import mark_safe
import markdown
@register.filter(name='markdown')
def markdown_format(text):
return mark_safe(markdown.markdown(text))
We register template filters in the same way as template tags. To avoid a collision between our function name and the markdown module, we name our function markdown_format and name the filter markdown for usage in templates, such as {{ variable|markdown }}. Django escapes the HTML code generated by filters. We use the mark_safe function provided by Django to mark the result as safe HTML to be rendered in the template. By default, Django will not trust any HTML code and will escape it before placing it in the output. The only exceptions are variables that are marked as safe from escaping. This behavior prevents Django from outputting potentially dangerous HTML and allows you to create exceptions for returning safe HTML.
Now, load your template tags module in the post list and detail templates. Add the following line at the top of the blog/post/list.html and blog/post/detail.html templates after the {% extends %} tag:
{% load blog_tags %}
In the post/detail.html templates, take a look at the following line:
{{ post.body|linebreaks }}
Replace it with the following one:
{{ post.body|markdown }}
Then, in the post/list.html file, replace the following line:
{{ post.body|truncatewords:30|linebreaks }}
Then, swap it with the following one:
{{ post.body|markdown|truncatewords_html:30 }}
The truncatewords_html filter truncates a string after a certain number of words, avoiding unclosed HTML tags.
Now, open http://127.0.0.1:8000/admin/blog/post/add/ in your browser and add a post with the following body:
This is a post formatted with markdown
--------------------------------------
*This is emphasized* and **this is more emphasized**.
Here is a list:
* One
* Two
* Three
And a [link to the Django website](https://www.djangoproject.com/)
Open your browser and take a look at how the post is rendered. You should see the following output:
As you can see in the preceding screenshot, custom template filters are very useful to customize formatting. You can find more information about custom filters at https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/#writing-custom-template-filters.