📜 ⬆️ ⬇️

Django templates. Inheritance.

Read the article " Fragmentary caching in MVC web frameworks ". The article describes the problem of caching the display fragment, namely the problem of complete separation of the controller and the display — the controller works out completely before the display call. If we cache a fragment in the display, it doesn’t change anything - the controller has already worked! The article describes how to avoid this: make the data request "lazy".

Starting to write how it should be done correctly, I decided to write how the Django templates are arranged, so that non-jang drivers would also understand.

How is it done in django?
')


Django Pattern Structure


The controls for the Django templates are variables, filters, and tags.

When rendering a template, the variables are replaced with their value calculated in the context of the call. Syntax - double braces - for example: {{ title }} .

Filters are used for simple variable transformations. The syntax is the |_:"" . The filter can occur both in variables and as a tag parameter. For example: {{ title|lowercase }} .

When rendering a template, the tags, roughly speaking, are replaced by the results of the associated function with this tag function on python. Syntax: {% %} , for example: {% url blog_article slug=article.slug %} .

A programmer can write his own filters and tags, but more on that later.

In addition, there are three special tags: include , block and extend . The include tag include requested template (rendered in the current context).

Everything listed above is trivial and in one form or another is in any template engine. We now turn to the features of Django: on the block and extend tags, the inheritance of templates is built. Let us dwell on them in more detail.

Pattern Inheritance


The main feature of Django templates is inheritance. The template can expand (refine) the behavior of the parent template.

Any part of the template can be wrapped in a block tag (of course, the tag cannot begin before, but end inside the loop). The block is given a name. For example:
 {% block content%}
	 block body
 {% endblock%}


Using the extend tag, we specify which template we will specify. By extending the template, we can override any blocks that are in the parent template. Anything outside these blocks will be skipped.

It turns out a powerful mechanism, virtually eliminating the need to repeat parts of the patterns. In brief, this is described in the documentation (see links at the end of the article). Let's take a real example.

Template inheritance example


Suppose we want to make a website that contains simple pages and a blog.

From the layout designer, we received a page layout containing:



Here's what it looks like:
 {% block head%}
	 {% block title%} {% endblock%}
	 {% block menu%} {% endblock%}
 {% endblock%}

 {% block page%}
	 {% block content%}
	 {% endblock%}
 {% endblock%}

 {% block footer%}
	 {% block copyright%}
	 {% endblock%}
 {% endblock%}



For all the specified elements, we create the corresponding block tags.

A simple page fits into this layout — it has only a heading and a body.

We now turn to the blog. In the blog I would like to add the right column to display a list of tags and recent articles. Maybe we want to add the right column to some other pages of the site. To avoid copying "two columns", we will move it into a separate template, redefining the page body at the base one.
 {% extend "base.htm"%}

 {% block page%}
	 {% block content%}
	 {% endblock%}

	 {% block sidebar%}
	 {% endblock%}
 {% endblock%}



The blog will have several types of pages:


In all pages, the right column remains unchanged, so it is reasonable to make a basic page for a blog, inheriting it from a two-column base page.
 {% extend "base_2col.htm"%}

 {% block title%}
	 Blog
 {% endblock%}

 {% block sidebar%}
	 {% block tags%}
	 {% endblock%}

	 {% block recent%}
	 {% endblock%}
 {% endblock%}


Now we give examples of the internal pages of the blog (all of them are inherited from the base page of the blog).

List of articles:
 {% extend "blog / base.htm"%}

 {% block content%}
	 {% for article in article_list%}
		 "A href =" {% url article_view article.id%} ""
		 {{article.title}}
		 "/ A"
	 {% endfor%}
 {% endblock%}



Article:
 {% extend "blog / base.htm"%}

 {% block title%}
	 {{article.title}} - {{block.super}}
 {% endblock%}

 {% block content%}
	 {{article.text}}
 {% endblock%}



List of articles that have a specific tag:
 {% extend "blog / index.htm"%}

 {% block title%}
	 {{tag.title}} - {{block.super}}
 {% endblock%}

 {% block content%}
	 {{tag.title}}
	 {{tag.text}}

	 {{block.super}}
 {% endblock%}


In this case, we used another trick. After all, this list is no different from a simple list of articles - it is simply filtered by additional parameters. Therefore, we inherited it from the list of articles and used the {{ block.super }} tag when overlapping the body - to output the entire contents of the parent block.

As you can see, each template is very specific and is responsible only for its functionality. He does not need to know about the whole page.

Fans of other template systems will say that for the given example inheritance is not needed. Indeed, the same can be done using substitution tags ( include , ssi ). But the logic and structure of these inclusions will be much more complicated. It turns out that the article should know what blocks will be on its page, and provide data for all these blocks. This is where another feature of Django comes in - custom tags.

Custom tags


In our example, there are 7 blocks on the blog article page. Two of them - the logo and copyright - do not need data. For the remaining five, the controller must provide data to the template. Namely:


Blocks could be much more, but only the title and body of the article are directly related to the article. Why does an article need to know what data these blocks need, how and where to get it from? Absolutely no reason - this is not her task. Django offers us the following solution to this problem.

For each of the blocks, we can write our own tag, consisting of a mini-controller and a template. The controller knows how to get the data, the template - how to display. In the place where we need a block, we insert its tag - and that's it! For example, you can insert a list of tags and recent articles on the main page. The main page does not need to know anything about the structure of our blog - just the fact of presence and the names of tags implemented by the blog.

Here is an example tag to display a list of the latest articles in the blog:
 #tag
 @ register.inclusion_tag ('blog / article_recent_list.htm')
 def blog_recent_articles_list ():
	 return {
		 'article_list': Article.objects.filter (public = True) [: 10],
	 }

 # template
 “H4” Recent articles “/ h4”
 "Ul class =" links "
	 {% for article in article_list%}
	 "Li" "a href =" {% url article%} "" {{article.title}} "/ a" "/ li"
	 {% endfor%}
 "/ Ul"


Another advantage of this approach is that the data is requested directly when the tag is inserted. If several tags are cached, then the results of their work will be cached - they will not be requested again! And do not reinvent the wheel, as here ;)

It is clear that with a careful approach, the same can be done without inheritance and without user tags — connections and function calls. The main advantage of Django is a slim, logical and standard structure for solving such problems.

Links to official documentation:


PS: This is a reprint from my blog http://dpp.su/ . By the way, there are some less interesting articles about Django.

Source: https://habr.com/ru/post/23132/


All Articles