Django Blog #28: Creating Template Engine Tags

by Alex
Django Blog #28: Creating Template Engine Tags

Django offers many built-in tags, such as {% if %} or {% block %}. Some of them were used in the previously discussed templates. The full list is available at: https://docs.djangoproject.com/en/2.0/ref/templates/builtins/. However, Django allows you to create your own template tags to do whatever you want. These will come in handy in cases where the functionality you need is not found among Django’s base set.

Creating a tag

Django offers the following helper features that make it easy to create your own template tags:

  • simple_tag: processes the data and returns a string.
  • inclusion_tag: processes the data and returns the rendered pattern.

Template tags can only be used within Django applications. In the blog application folder, create a new folder, name it templatetags, and add an empty __init__.py file. Create another file blog_tags.py. The file structure of the blog application should be as follows:

blog/ 
    __init__.py 
    models.py 
    .. 
    templatetags/ 
        __init__.py 
        blog_tags.py

It is important to choose the correct names because they will be used to load the tags into templates. Let’s start by creating a simple tag that will get the total number of posts published on the blog. Edit the blog_tags.py file and add the following code:

from django import template  
from.models import Post  
  
  
register = template.Library() 
  
  
@register. simple_tag  
def total_posts() 
   return Post.published.count()

This tag returns the number of posts published. Each template tag module, in order to be a valid tag library, must include a variable register. This variable is an instance of template.Library. It is used to register your own template tags and filters. Next you need to define the tag total_posts using the Python function and use the @register.simple_tag decorator to register it as a simple tag. Django uses the function name as the tag name. If you want to choose a different name, you can do so using the name attribute, such as @register.simple_tag(name='my_tag').

After adding a new template tag module, you need to restart your Django development server so that the changes take effect and the new tags and filters can be used.

Before you start using your own template tags, you need to make them available using the {% load %} tag. As mentioned earlier, you should use the name of the Python module that includes the template tags and filters. Open the template and add {% load blog_tags %} to the top to load the template tag module. Then use the created tag to display the total number of posts published. Just add a {% total_posts %} tag to the template. It will look like this:

{% load blog_tags %}  
{% load static %}  
<!DOCTYPE html>  
<html>  
<head>  
 <title>{% block title %}{% endblock %}</title>  
 <link href="{% static 'css/blog.css' %}" rel="stylesheet">  
</head>
<body>  
 <div id="content">  
  {% block content %}  
      {% endblock %}  
    </div>  
 <div id="sidebar">  
 <h2>My blog</h2>  
 <p>This is my blog. I've written {% total_posts %} posts so far.</p>  
 </div>
</body>  
</html>

You need to restart the server so that the project keeps track of all new files. Stop it with Ctrl + C and start again with:

python manage.py runserver
Open https://127.0.0.1:8000/blog/ in your browser. The total number of posts published will be displayed:

Django Blog #28: Creating Template Engine TagsThe advantage of custom tags is that you can use them to process any data and add it to any template, regardless of presentation. You can use QuerySet or process any data to display results in templates. Now let’s create another tag to display the latest posts in the sidebar. We will use the inclusion tag. It will render the template with the context variables the template tag returns. Edit the blog_tags.py file and add the following code:

@register.inclusion_tag('blog/post/latest_posts.html') 
def show_latest_posts(count=5) 
    latest_posts = Post.published.order_by('-publish')[:count] 
   return {'latest_posts': latest_posts}

In this code, the template tag has been registered with @register.inclusion_tag. A template has also been defined, which the tag will return along with the values with blog/post/latest_posts.html. The tag will accept the optional count parameter with a default value of 5. It allows you to specify the number of posts to output. Let’s use a variable to limit the results of the query Post.published.order_by('-publish')[:count]. Note that the function returns a dictionary, not a single value. The included tag should return a dictionary of values, which is used as the context for rendering a particular template. The created template tag allows you to define the number of posts to display as desired: {% show_latest_posts 3 %}.

Now you need to create a new template file in your blog/post/ folder and name it latest_posts.html. Add the following code there:
<ul>  
{% for post in latest_posts %}  
  <li>  
    <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>  
  </li>
{% endfor %}  
</ul>

This code displays an unordered list of posts using the latest_posts variable returned by the template tag. Now you need to edit the blog/base.html template and add a template tag to display the last three posts. The code for the sidebar should look like this:

<div id="sidebar">  
  <h2>My blog</h2>  
  <p>This is my blog. I've written {% total_posts %} posts so far.</p>  
  <h3>Latest posts</h3>  
  {% show_latest_posts 3 %}  
</div>

The template tag is called, passing the number of posts to be displayed, and the template is rendered at the desired position with the specified context. Return to the browser and refresh the page. The sidebar will look like this:Django Blog #28: Creating Template Engine TagsFinally, let’s create a simple template tag that saves the result in a variable so that it can be used later. Let’s create a tag to display the most commented posts. Edit the blog_tags.py file and add the following import and template tag:

from django.db.models import Count

# ...

@register. simple_tag  
def get_most_commented_posts(count=5) 
   return Post.published.annotate( 
        total_comments=Count('comments') 
   ).order_by('-total_comments')[:count]

In it, use annotate() to create a QuerySet that aggregates the total number of comments under the posts. Now use the Count aggregation function to store the number of comments and the total_comments field of each Post object. Let’s sort the QuerySet by the value of this field in descending order. Let’s use the count variable to limit the total number of returned objects. In addition to Count, Django has aggregation functions like Avg, Max, Min, and Sum. You can read about them here https://docs.djangoproject.com/en/2.0/topics/db/aggregation/. Edit the blog/base.html template and add the following code to the <div> sidebar element (after {% show_latest_posts 3 %}):

<h3>Most commented posts</h3>  
{% get_most_commented_posts as most_commented_posts %}  
<ul>  
  {% for post in most_commented_posts %}  
  <li>  
    <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>  
  </li>  
  {% endfor %}  
</ul>

Save the result to a variable using the as argument and the variable name. For the template tag, use {% get_most_commented_posts as most_commented_posts %} to save the result of the template tag in a new variable most_commented_posts. Then display the posts as an unsorted list. Open the browser and reload the page to see the final result:Django Blog #28: Creating Template Engine Tags Now you know how to create template tags yourself. You can read more about them here https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/.

Related Posts

LEAVE A COMMENT