How To Register Tags Django
Django templates help you manage your spider web application'southward HTML. Templates use a mini-language with variables, tags, and filters. You can conditionally include blocks, create loops, and modify variables earlier they're shown. Django comes with many congenital-in tags and filters, but what if they're not enough? In that case, write your ain! This tutorial covers the ins and outs of writing your ain Django template custom tags and filters.
In this tutorial, you'll learn how to:
- Write and register a function as a custom filter
- Understand how autoescaping works in custom tags and filters
- Use
@simple_tag
to write a custom template tag - Apply
@inclusion_tag
to render a tag based on a subtemplate - Write a complex template tag with a parser and renderer
By the end of the tutorial, you'll be able to write custom filters to modify data in your templates and custom tags that give yous access to the full ability of Python within your templates.
Getting Started
To play around with your ain Django template custom tags and filters, you're going to need a Django projection. You'll build dinosoar, a small website with all sorts of dinosaur info. Although the proper name implies that you'll merely include flying dinos, that's only for marketing spin. All your favorite heavyweights will be there as well.
If you've never fix upwards a Django project before or if you demand a refresher, you may want to read Get Started With Django Function 1: Build a Portfolio App start.
Django is a third-party library, so it should be installed in a virtual surroundings. If you lot're new to virtual environments, check out Python Virtual Environments: A Primer. Create and activate a new virtual environment for yourself so run the following commands:
ane $ python -m pip install django == 3.2.5 2 $ django-admin startproject dinosoar iii $ cd dinosoar 4 $ python manage.py startapp dinofacts 5 $ python manage.py drift
These commands perform the following actions:
- Line 1 runs the
pip
command to install Django. - Line 2 creates your new Django project.
- Line 3 changes the electric current working directory to the
dinosoar
project. - Line iv uses the
manage.py
command to create a Django app calleddinofacts
, where your main view will live. - Line 5 migrates any database changes. Even if you aren't creating models, this line is necessary because the Django admin is agile by default.
With the project created, it'due south fourth dimension to make some configuration changes and write a quick view to aid you test your custom tags and filters.
Setting Upwards a Django Project
Yous demand to make some changes to your project's settings to make Django enlightened of your newly created app and to configure your templates. Edit dinosoar/dinosoar/settings.py
and add dinofacts
to the INSTALLED_APPS
list:
34 # dinosoar/dinosoar/settings.py 35 36 INSTALLED_APPS = [ 37 "django.contrib.admin" , 38 "django.contrib.auth" , 39 "django.contrib.contenttypes" , 40 "django.contrib.sessions" , 41 "django.contrib.messages" , 42 "django.contrib.staticfiles" , 43 "dinofacts" , 44 ]
Within the same file, you lot'll need to update the DIR
value in the TEMPLATES
aspect. This tells Django where to await for your template files:
57 # dinosoar/dinosoar/settings.py 58 59 TEMPLATES = [ sixty { 61 "BACKEND" : "django.template.backends.django.DjangoTemplates" , 62 "DIRS" : [ 63 BASE_DIR / "templates" , 64 ], 65 "APP_DIRS" : True , 66 "OPTIONS" : { 67 "context_processors" : [ 68 "django.template.context_processors.debug" , 69 "django.template.context_processors.request" , 70 "django.contrib.auth.context_processors.auth" , 71 "django.contrib.messages.context_processors.messages" , 72 ], 73 },
Starting with Django 3.1, the BASE_DIR
value that specifies where the projection lives is a pathlib
object. The alter to the DIRS
value in a higher place tells Django to wait in a templates/
subdirectory within your project directory.
With the settings changed, don't forget to create the templates/
directory within your project:
$ pwd /home/realpython/dinosoar $ mkdir templates
It'south fourth dimension to start writing some lawmaking. To exam your custom template tags and filters, yous'll need a view. Edit dinosoar/dinofacts/views.py
as follows:
one # dinosoar/dinofacts/views.py 2 three from datetime import datetime 4 from django.shortcuts import return 5 six def show_dino ( request , name ): 7 data = { 8 "dinosaurs" : [ 9 "Tyrannosaurus" , 10 "Stegosaurus" , 11 "Raptor" , 12 "Triceratops" , xiii ], 14 "at present" : datetime . at present (), xv } 16 17 render render ( request , name + ".html" , data )
Lines 7 to 15 create a lexicon with some sample information. Yous'll apply this in your templates to exam your tags and filters. The rest of this view does something a piffling unorthodox: it takes a parameter that specifies the name of a template.
The return()
office loads and renders a template. Here, the name
value gets suffixed with ".html"
, turning information technology into the proper name of a template to load. This is not something you'd normally practice, but the remainder of this article shows you lot a lot of templates. Rather than having to write a new view for each experiment, this single view will accomplish the task.
The last step before writing a template is to register your view every bit a URL. Edit dinosoar/dinosoar/urls.py
so that it looks like this:
1 # dinosoar/dinosoar/urls.py 2 iii from django.urls import path 4 five from dinofacts.views import show_dino 6 vii urlpatterns = [ 8 path ( "show_dino/<str:name>/" , show_dino ), 9 ]
Line 8 registers the /show_dino/
URL with the show_dino
view. This URL expects an argument called proper noun
, which volition be turned into the name of the template to load inside the view.
Using Django Templates
With the view in place, you're all set to render some HTML. The side by side step is to create a template. Almost HTML pages are rather repetitive, containing boilerplate header information, meta-info about the page, and navigation tools. Django templates apply the ability of inheritance to minimize the repetition. To see this in action, create dinosoar/templates/base.html
:
one <!-- dinosoar/templates/base.html --> ii 3 < html > 4 < body > v {% cake content %} 6 < h1 >Dino Soar</ h1 > 7 {% endblock content %} eight </ body > 9 </ html >
By defining blocks and extending parent templates, you lot can avert a lot of the cookie-cutter duplication that often happens in HTML files across a site. The template above is a base for all time to come templates in this tutorial.
Many of the examples in this tutorial volition be in their own template files. You'll be creating each ane, and each will inherit from the base of operations file above. The first case child file you need to create is dinosoar/templates/eoraptor.html
:
i <!-- dinosoar/templates/eoraptor.html --> 2 3 {% extends "base.html" %} four 5 {% block content %} half dozen 7 < h1 >Eoraptor</ h1 > 8 9 < p > 10 Eoraptor was ane of the earliest dinosaurs and its name xi is based on the Greek word for "dawn". 12 13 {% comment %} Add something about tiptop here {% endcomment %} 14 fifteen </ p > 16 17 < p > 18 Some other popular dinosaurs were: 19 20 < ul > 21 {% for dino in dinosaurs %} 22 < li > {{ dino | lower }} </ li > 23 {% endfor %} 24 </ ul > 25 </ p > 26 27 {% endblock content %}
This template uses a few common built-in template tags and filters:
- Line iii declares that this template inherits from
"base.html"
by using the{% extends %}
tag. - Lines five and 27 declare a block chosen
content
. During the rendering ofeoraptor.html
, the template engine looks for a cake with the same name in its parent. The engine replaces the corresponding block inbase.html
. - Line 13 uses the
{% comment %}
tag to write a annotate. The rendered output volition not include the contents of this tag. - Lines 21 and 23 define a
{% for %}
block tag. This works like afor
loop in Python. Information technology iterates over the valuedinosaurs
and renders the line within the block for each member. - Line 22 is rendered in one case for each particular in the
dinosaurs
list. Each value gets put inside HTML<li>
tags. Note thelower
filter used with the value. Filters get practical to a value through the pipe (|
) symbol. Filters use a value so modify the rendered result. Thelower
filter is like to the.lower()
method in Python, rendering the value as lowercase.
Everything is in place now. Run the Django development server to see the result:
$ python manage.py runserver
To encounter the view, visit http://127.0.0.ane:8000/show_dino/eoraptor/
. Your outcome should wait like this:
The annotate has disappeared, and the list of dinosaurs is in lowercase.
You can find more data past going over the Django documentation about built-in template tags and filters or by checking out Django Templates: Built-In Tags and Filters.
Loading Modules and Third-Political party Tag Libraries
There are over seventy-five tags and filters built into Django and several modules on top of that, merely that still might non exist plenty for your use instance. But before writing your ain custom tags and filters, you should first practise some enquiry to see if another package meets your needs.
To utilize a tag or filter that comes with Django merely isn't in the standard set, you need to:
- Register the package's Django app
- Load the template library into your template
Many tertiary-party packages are bachelor as well. Using a third-party package is no different, except that y'all'd need to install the package first using pip
.
A popular parcel that comes with Django but isn't function of the congenital-in library is humanize
. This library has filters that change numeric data into more readable forms. Because it ships as office of the contrib
module in Django, there'south no additional installation step.
To register the app, update your INSTALLED_APPS
list in dinosoar/dinosoar/settings.py
:
32 # dinosoar/dinosoar/settings.py 33 34 INSTALLED_APPS = [ 35 "django.contrib.admin" , 36 "django.contrib.auth" , 37 "django.contrib.contenttypes" , 38 "django.contrib.sessions" , 39 "django.contrib.messages" , 40 "django.contrib.staticfiles" , 41 "dinofacts" , 42 "django.contrib.humanize" , 43 ]
Create a new template called dinosoar/templates/iggy.html
:
ane <!-- dinosoar/templates/iggy.html --> two 3 {% extends "base.html" %} iv {% load humanize %} 5 half-dozen {% block content %} 7 8 < h1 >Iguanodon</ h1 > nine 10 < p > 11 Iguanodon (iguana-tooth) were large herbivores. They weighed 12 {{ 3080 | intcomma }}kg ({{ 6800 | intcomma }}lbs). 13 Wow, {{ 3080000 | intword }} grams is a lot! 14 15 </ p > 16 17 {% endblock content %}
The key to using a tag or filter that isn't congenital-in is the {% load %}
tag you utilise on line 4. This is the equivalent of an import
statement in your Python lawmaking.
The iggy.html
template uses two filters from humanize
:
-
intcomma
converts a number into a cord with a comma every three digits. -
intword
converts large numbers into their English equivalent.
Visit http://127.0.0.1:8000/show_dino/iggy/
to encounter the effect:
In that location are a lot of tags in Django, both built-in and included in the contrib
module. There are fifty-fifty more than tertiary-party libraries out there. But what if y'all tin't discover something that solves your trouble? It's time to practice it yourself by writing some Django custom tags and filters.
Building Tags and Filters
Custom tags and filters live in your Django app in a templatetags/
directory. Yous can import any files in this directory into a template using the {% load %}
tag. The proper noun of the module you lot create will exist the name you apply to load the tag library.
For this project, the structure volition await like this:
dinosoar/dinofacts/templatetags/ ├── __init__.py └── dinotags.py
This directory is a module like any other Python code, and then information technology requires the __init__.py
file. The dinotags.py
file volition contain all the lawmaking for your custom tags and filters.
As you lot'll before long explore more, filters are functions. Tags tin can be either functions or classes, depending on their complexity. Just writing the functions and classes isn't sufficient—you also need to register the code with Django.
Registration requires an example of a Library
class, which you tin can then apply equally a decorator to wrap your tags and filters. The following code is a simplified version of the built-in filter lower
:
1 from django import template 2 3 register = template . Library () 4 5 @register . filter 6 def lower ( value ): 7 return value . lower ()
Think about how this example works:
- Line 1 imports Django's
template
module. - Line 3 creates an case of
Library
used for registration. - Line 5 uses the
Library
case'sfilter
method every bit a decorator. This tells Django that thelower()
role is a filter. - Lines half dozen to 7 ascertain the role implementing the filter. The filtered value is the first statement to the function. This implementation assumes the value is a cord. The string's
.lower()
method gets called and the effect returned. What yous return from your filter function gets rendered in the template.
The Library
object provides methods for registering tags and filters. You may call these methods directly, but the better fashion is to utilise them as decorators. Decorating the function makes it clear to other programmers that it'southward registered as a tag or filter.
Writing Django Template Custom Filters
You lot prepare your project and wrote a view to use for testing. Then, you used congenital-in tags and filters as well as loaded tags from a library. In the previous section, y'all learned how to annals tags and filters. In this section, you're all set to write your first Django custom filter!
Filters as Functions
As mentioned previously, filters are Python functions. The most basic filters have a single statement: the value to filter. The upshot of a filter function gets rendered past the template engine.
To get started, you'll write a filter that renders a cord composed of the first letter of the alphabet of each particular in a list. If you haven't already, you'll need to gear up your template tag file:
$ pwd /home/realpython/dinosoar $ mkdir dinofacts/templatetags $ touch dinofacts/templatetags/__init__.py
With the construction in place, either create or edit the template tag file called dinosoar/dinofacts/templatetags/dinotags.py
:
1 # dinosoar/dinofacts/templatetags/dinotags.py two 3 from django import template four 5 register = template . Library () 6 7 @register . filter 8 def first_letters ( iterable ): 9 event = "" 10 for particular in iterable : 11 result += item [ 0 ] 12 xiii return result
The code above registers a filter called first_letters
. The function expects an iterable, similar a list. Information technology iterates over the list and builds the effect
cord. If the value to filter is a list of strings, then result
is the start alphabetic character of each of those strings.
To use this filter in action, create dinosoar/templates/raptor.html
:
one <!-- dinosoar/templates/raptor.html --> 2 3 {% extends "base.html" %} 4 {% load dinotags %} 5 half-dozen {% block content %} vii eight < h1 >Velociraptor</ h1 > 9 10 < p > 11 The Velociraptor (swift seizer) was made famous by their appearance 12 in the motion-picture show < i >Jurassic Park</ i >. Different in the film, these 13 dinosaurs were smaller, about the size of a turkey. They shared xiv something else with turkeys: they likely had feathers. 15 16 </ p > 17 < p > 18 The first letters of our dinosaur variable are {{ dinosaurs | first_letters }}. xix 20 </ p > 21 22 {% endblock content %}
With your template in place, visit http://127.0.0.1:8000/show_dino/raptor/
to meet the result:
Remember from dinofacts/views.py
that the dinosaurs
value is a list containing "Tyrannosaurus"
, "Stegosaurus"
, "Raptor"
, and "Triceratops"
. The issue above is the offset letter of each of these mighty reptiles: "TSRT"
.
Filters tin can also take arguments. Now you'll augment the ability of first_letters
by writing a filter that returns the north-thursday letter of each item in an iterable. Add this function to dinotags.py
:
23 # dinosoar/dinofacts/templatetags/dinotags.py 24 25 @register . filter ( name = "nth_letters" , is_safe = True ) 26 def other_letters ( iterable , num ): 27 result = "" 28 for particular in iterable : 29 if len ( detail ) <= num or not item [ num - 1 ] . isalpha (): 30 upshot += " " 31 else : 32 event += item [ num - 1 ] 33 34 render result
In that location are a few new things going on here:
- Line 25 adds the
name
argument to the@register.filter()
decorator. This makes the filter'southward name in the template different from the implementing part. Here, the filter gets namednth_letters
fifty-fifty though the role that implements it isother_letters()
. Notation thatis_safe=Truthful
indicates to Django that the output of this filter doesn't contain characters that will break HTML. You'll explore more almost this below. - Line 26 defines the function. The value to filter is the first argument, and the filter'southward parameter is the second.
- Lines 28 to 32 iterate over the value and build the resulting cord.
- Line 29 is a safety check. If yous're looking for the tenth index in an 8-alphabetic character string, it'll use a infinite (
" "
) instead. Likewise, if the north-thursday grapheme isn't a letter, y'all use a space to avoid accidentally returning characters that intermission HTML. - Line 34 returns the
result
string to be rendered.
Using strings inside of HTML safely is a deep topic. HTML consists of strings with sure characters irresolute how the browser displays the page. You lot have to be conscientious about what string data you pass to the rendering engine, and doubly so if the information was user input.
Django extends raw Python strings with a class chosen SafeString
. A SafeString
object has additional information in it that indicates whether the template engine should escape it before rendering.
When Django renders a template, parts of the template may be in autoescape mode. These areas automatically escape the values within, then Django will turn whatever troublesome characters into the corresponding HTML entity for brandish. Sometimes the values you're rendering are supposed to contain HTML, and so they need to exist marked safe.
In the case above, the is_safe=True
argument to the registration decorator tells Django that this filter promises not to output any troublesome characters. A prophylactic cord passed to a filter does not get escaped by Django. The default value for is_safe
is Fake
.
Notation that is_safe=True
is not marking your filter result as safe. That's a separate step for which you are responsible. The call to .isalpha()
above ensures that all output from this function is rubber, so there'south no need for an extra pace.
Exist careful when determining whether your filter is rubber or not, especially when removing characters. A filter that removed all semicolons would pause HTML entities that depend on semicolons, like &
.
To play with the nth_letters
filter, create dinosoar/templates/alberto.html
:
1 <!-- dinosoar/templates/alberto.html --> 2 3 {% extends "base.html" %} 4 {% load dinotags %} v 6 {% cake content %} seven eight < h1 >Albertosaurus</ h1 > 9 ten < p > eleven Albertosaurus ('Alberta lizard') is a smaller cousin of 12 the T-Rex. These dinosaurs were named afterwards the location 13 of their first discovery, Alberta, Canada. 14 15 </ p > sixteen 17 < p > 18 The nth letters of our dinosaur variable are: 19 < ul > 20 < li > tertiary: "{{ dinosaurs | nth_letters : iii }}"</ li > 21 < li > fifth: "{{ dinosaurs | nth_letters : 5 }}"</ li > 22 < li > 10th: "{{ dinosaurs | nth_letters : 10 }}"</ li > 23 </ ul > 24 </ p > 25 26 {% endblock content %}
Visit http://127.0.0.1:8000/show_dino/alberto/
to get the resulting HTML:
Explore the text and check that your nth_letters
filter works how yous'd expect.
The Canadian lizard might non take been king, but yous probably all the same wouldn't want to meet it in a dark alley.
String Filters
The information blazon of a filter'south argument is the data blazon of the value used inside the template. The most mutual data type within HTML documents is the cord. Django provides a style to coerce the filter's input into a string, and so y'all don't accept to do this manually. Now you'll write a new filter that outputs a judgement summarizing the number of instances of a letter in a cord.
Add together the following to your dinotags.py
file:
35 # dinosoar/dinofacts/templatetags/dinotags.py 36 37 from django.template.defaultfilters import stringfilter 38 from django.utils.html import conditional_escape , mark_safe 39 40 @register . filter ( needs_autoescape = True ) 41 @stringfilter 42 def letter_count ( value , letter , autoescape = True ): 43 if autoescape : 44 value = conditional_escape ( value ) 45 46 upshot = ( 47 f "<i> { value } </i> has <b> { value . count ( letter ) } </b> " 48 f "instance(s) of the letter of the alphabet <b> { letter } </b>" 49 ) 50 51 return mark_safe ( consequence )
The @stringfilter
decorator on line 41 indicates that this filter only takes strings. Django turns the filter's value into a string before passing it into the filter. At that place are some other interesting things going on in this function:
- Line 40 uses the
needs_autoescape
parameter in the registration decorator. This tells Django to add together some other argument to the filter part:autoescape
. The value of this argument will bespeak whether autoescaping is on or off for the telescopic of this filter. - Line 42 declares the filter function and includes the
autoescape
argument mentioned higher up. This statement should default toTrue
so that you're in autoescape mode if your code calls the office directly. - Lines 43 to 44 replace
value
with the outcome ofconditional_escape()
ifautoescape
isTruthful
. Theconditional_escape()
part escapes the string only is smart enough to not escape something that has already been escaped. - Lines 46 to 49 build the returning string. Because the
letter_count
filter outputs HTML with bold and italic tags, it has to be autoescape-aware. The f-string on line 47 uses the contents ofvalue
, which got appropriately escaped in lines 43 to 44, as needed. Theresult
string containsvalue
in italics and the letter count in bold. - Line 51 calls
mark_safe()
on theissue
variable. Considering the filter is outputting HTML that should be displayed, the office must mark the cord as safe. This tells Django not to further escape the contents so the assuming and italic tags get rendered by your browser.
To test out this filter, create the following in dinosoar/templates/mosa.html
:
1 <!-- dinosoar/templates/mosa.html --> two three {% extends "base.html" %} 4 {% load dinotags %} five vi {% cake content %} 7 8 < h1 >Mosasaurus</ h1 > 9 x < p > 11 Mosasaurus ('Meuse River lizard') was an aquatic reptile that lived in 12 the Late Cretaceous. Estimated lengths reach upward to 17 meters xiii (56 feet)! {{ "Mosasaurus" | letter_count :"south" }} 14 15 </ p > sixteen 17 {% endblock content %}
Fire upward your evolution server and get to http://127.0.0.ane:8000/show_dino/mosa/
to see the following results:
The @stringfilter
decorator is a quick shortcut that ensures your filter will just have to deal with strings. The needs_autoescape
argument and its corresponding autoescape
argument give you fine-grained control over what the filter does and doesn't autoescape.
Date Filters
Dates and time zones can be tricky things to deal with. Dealing with them on a website has an added wrinkle: whose fourth dimension zone? The server's? The user'southward? Something else?
Django has built-in tools to help deal with this problem. Role of Django'south solution is two central settings:
-
USE_TZ
-
TIME_ZONE
When USE_TZ
is True
, Django does all date work co-ordinate to the time zone yous set in TIME_ZONE
. The default setting for this is UTC.
It'due south like shooting fish in a barrel to forget that template rendering happens on the server-side. Each company is getting their own page rendered, so information technology's natural to remember of the browser as being responsible. Nevertheless, since rendering does happen on the server, the server'due south time zone is the time zone used—unless Django's settings brand information technology otherwise. Neither the server's time zone nor Django'due south settings have to correspond to the user'southward fourth dimension zone.
This makes filters and dates complicated. To aid with this, filter registration supports an argument chosen expects_localtime
. Django converts datetime
objects into the configured time zone when expects_localtime
is True
. To see how this works, add together the following lawmaking to dinotags.py
:
57 # dinosoar/dinofacts/templatetags/dinotags.py 58 59 @annals . filter ( expects_localtime = Truthful ) threescore def bold_time ( when ): 61 return mark_safe ( f "<b> { when } </b>" )
This filter returns a bolded version of the passed in datetime object. There are better ways of doing this without a filter, but that wouldn't testify you lot the fourth dimension zone effects. With expects_localtime
set to True
in the code in a higher place, Django will render the page with a datetime object moved into the time zone specified past the TIME_ZONE
setting. To play around with this, create dinosoar/templates/ptero.html
:
one <!-- dinosoar/templates/ptero.html --> 2 three {% extends "base.html" %} 4 {% load dinotags %} v 6 {% block content %} 7 8 < h1 >Pterodactyl</ h1 > 9 10 < p > 11 Pterodactyl ('winged finger') is the common name for Pterodactylus, 12 the get-go of the genus pterosaur to exist identified as a flying 13 reptile. This species is thought to have gone extinct 150 1000000 14 years ago, which is a long fourth dimension earlier at present ({{ now | bold_time }}). 15 16 </ p > 17 xviii {% endblock content %}
Visit the page http://127.0.0.one:8000/show_dino/ptero/
to see the filter in action:
To see the departure, edit dinosoar/dinosoar/settings.py
and modify the values of USE_TZ
or TIME_ZONE
and reload the folio. Depending on your choices, the time and possibly even the date will change.
Custom filters give you fine-grained control over your HTML output. They enable you to change the appearance of your information through reusable components. Even so, as filters are data-focused, they're express. To accept consummate control over a block, you lot need custom tags.
Writing Django Template Custom Tags
Filters operate on a unmarried value modifying how they're rendered. Tags are much more than flexible than that, allowing you to inject or modify blocks of content besides as manipulate the data context.
Like filters, you piece of work with tags by:
- Declaring them in modules within an app'south
templatetags/
directory - Registering them using a
Library
instance - Implementing them as functions
Additionally, for more than complex tags, you can apply rendering classes instead of functions. This is necessary to implement tags that render blocks.
Using Simple Tags
To make tag writing more straightforward, Django has the @simple_tag
decorator. Structurally, this is similar to a filter: yous register a function as a tag, and Django renders its return value. Dissimilar the filter, tags don't get associated with values. They're on their own. You'll start with the simplest of simple tags by editing the dinosoar/dinofacts/templatetags/dinotags.py
file:
57 # dinosoar/dinofacts/templatetags/dinotags.py 58 59 @register . simple_tag 60 def mute ( * args ): 61 render ""
Django's {% comment %}
tag is a cake and requires a lot of typing. The tag above is a more basic version of the same thought: whatever gets passed into the tag as a parameter gets ignored, and Django renders the tag as an empty string. Create dinosoar/templates/rex.html
to examination your mute
tag:
i <!-- dinosoar/templates/king.html --> 2 3 {% extends "base.html" %} 4 {% load dinotags %} 5 6 {% cake content %} 7 viii < h1 >Tyrannosaurus {% mute "The Rex" %} Rex</ h1 > ix 10 < p > eleven Tyrannosaurus rex ('lizard-tyrant king'), or T-Rex for short, is the 12 largest of a genus of theropods. thirteen 14 It had very {% mute "chomp chomp chomp" %} big teeth. 15 </ p > 16 17 {% endblock content %}
Visit the folio http://127.0.0.1:8000/show_dino/rex/
to see the filter in activity:
There's not much to encounter in the results, and that's the point. Everything inside the mute
tag has been removed.
Escaping Content
Tags, like filters, have to be concerned with whether the content they're generating is safe for HTML or not. Tags created with @simple_tag
are automatically autoescaped but still need to have their content marked safety if information technology contains HTML.
Consider the following tag, which takes a listing and renders an HTML bulleted listing. Open dinotags.py
and add the post-obit function:
71 # dinosoar/dinofacts/templatetags/dinotags.py 72 73 from django.utils.html import escape , mark_safe 74 75 @register . simple_tag 76 def make_ul ( iterable ): 77 content = [ "<ul>" ] 78 for detail in iterable : 79 content . append ( f "<li> { escape ( item ) } </li>" ) 80 81 content . append ( "</ul>" ) 82 content = "" . join ( content ) 83 return mark_safe ( content )
This part takes an iterable—such equally a list—and wraps each of its items in an HTML <li>
block. Note the utilise of escape()
on line 79. You don't desire to trust what's passed into the tag. The content
variable is a list that starts with a <ul>
tag, has each item appended, and and then ends with the corresponding closing </ul>
tag. Everything gets joined together into a string, and the string gets marked condom.
Use make_ul
in a template past creating dinosoar/templates/bronto.html
:
1 <!-- dinosoar/templates/bronto.html --> 2 iii {% extends "base of operations.html" %} 4 {% load dinotags %} 5 6 {% block content %} 7 viii < h1 >Brontosaurus</ h1 > ix 10 < p > 11 Brontosaurus (thunder lizard) is a long necked quadruped whose being 12 was debated for a long fourth dimension, with original finds being found to be 13 equanimous of different animals. In 2015 the proper noun was resurrected subsequently fourteen an extensive study showed that there was a distinction between it and fifteen its cousin the Apatosaurus. xvi 17 </ p > eighteen 19 < h2 >Other Dinosaurs</ h2 > 20 21 {% make_ul dinosaurs %} 22 23 {% endblock content %}
Visit http://127.0.0.1:8000/show_dino/bronto/
to go the result:
Play around with the dinosaurs
value from the show_dino()
view inside dinosoar/dinofacts/views.py
to see how escaping works. For example, add together bold tags to "Tyrannosaurus"
to brand information technology "<b>Tyrannosaurus</b>"
, and you'll see the tags displayed instead of really making anything assuming.
Working With the Context
When your view renders a template, y'all can pass information to the template engine through a dictionary called Context
. All values rendered in a page come up from the Context
object, and you can get and ready them from within a tag. Create a new tag in dinotags.py
:
87 # dinosoar/dinofacts/templatetags/dinotags.py 88 89 @register . simple_tag ( takes_context = True ) 90 def dino_list ( context , championship ): 91 output = [ f "<h2> { title } </h2><ul>" ] 92 for dino in context [ "dinosaurs" ]: 93 output . append ( f "<li> { escape ( dino ) } </li>" ) 94 95 output . append ( "</ul>" ) 96 output = "" . bring together ( output ) 97 98 context [ "weight" ] = "20 tons" 99 render mark_safe ( output )
This lawmaking is similar to make_ul
but with a few key changes:
- Line 89 adds the
takes_context
statement to the tag registration telephone call. Setting this toTrue
tells Django to add together an argument to the tag's function phone call that contains theContext
object. - Line 90 declares the function for the tag. Note that the
context
argument comes first. Tags can accept a variable number of arguments, socontext
has to come start. - Line 92 accesses the
context
argument as a dictionary, getting the value fordinosaurs
, which is the same list of dinosaurs used in many other examples. - Line 98 writes the string
"20 tons"
to the context using the key"weight"
.
Create a new file called dinosoar/templates/apato.html
to examination this tag:
1 <!-- dinosoar/templates/apato.html --> ii 3 {% extends "base.html" %} iv {% load dinotags %} 5 6 {% cake content %} 7 8 < h1 >Apatosaurus</ h1 > 9 ten < p > 11 Apatosaurus (deceptive cadger) is a long necked quadruped that when 12 originally discovered was confused with parts of what is now called 13 a Brontosaurus. Apatosaurus weighed on average {{ weight }}. 14 xv </ p > 16 17 {% dino_list "Other Big Lizards" %} xviii 19 < p > twenty Let's endeavour this again: Apatosaurus weighed on average {{ weight }}. 21 </ p > 22 23 {% endblock content %}
Note the use of the value weight
on lines 13 and xx. The first use is before the {% dino_list %}
tag, and the second use is afterward. Equally the value for weight
is added as a side effect of the tag, the first instance should be undefined and therefore blank.
The scope of context changes is strictly within the rendering engine. Modifying the context dictionary doesn't affect any original value in the view. Endeavour out this template by going to http://127.0.0.1:8000/show_dino/apato/
:
Equally promised, the sentence Apatosaurus weighed on average ends rather abruptly. The value for weight
isn't assigned until the template engine renders the tag. The use of the context inside tags is a powerful tool for communicating between your tags or saving country for a tag that you run multiple times.
Writing Inclusion Tags
The template engine renders whatever your tag function returns. As you accept explored in previous examples, yous ofttimes write HTML snippets within your tags. Composing HTML inside strings can be cumbersome, and so inclusion tags requite yous another way to attain this: your tag itself can employ templates. To run across how this is done, outset past creating the subtemplate dinosoar/templates/sublist.html
:
1 <!-- dinosoar/templates/sublist.html --> 2 3 < ul > four {% for particular in iterator %} 5 < li > {{ particular }} </ li > 6 {% endfor %} 7 </ ul >
Your tag will employ this template. Now you tin can add the following tag role to dinotags.py
:
103 # dinosoar/dinofacts/templatetags/dinotags.py 104 105 @register . inclusion_tag ( "sublist.html" ) 106 def include_list ( iterator ): 107 return { "iterator" : iterator }
The combination of the sublist.html
template and this new tag achieves the same affair that make_ul
did, but with less code. The @inclusion_tag
decorator specifies which template to return with this tag, and the tag function returns a dictionary to apply as the context inside that template.
To see the results, create a test page called dinosoar/templates/brachio.html
:
1 <!-- dinosoar/templates/brachio.html --> 2 3 {% extends "base.html" %} 4 {% load dinotags %} 5 half dozen {% block content %} seven eight < h1 >Brachiosaurus</ h1 > 9 10 < p > eleven Brachiosaurus (arm lizard) is yet another long-necked quadruped. 12 thirteen </ p > 14 15 < h2 > Using include_list </ h2 > 16 {% include_list dinosaurs %} 17 18 {% endblock content %}
Go to the usual view to run across the page http://127.0.0.1:8000/show_dino/brachio/
:
If yous're writing a tag that uses a lot of HTML, using @inclusion_tag
is a ameliorate style to go on the HTML separate from the code.
Creating Advanced Custom Tags
Unproblematic tags are a quick way of writing a tag for the rendering engine to supersede inline. Something you tin can't practise with a simple tag is build blocked areas. Consider how {% comment %}
and {% endcomment %}
are paired together, removing everything between them. In this section, you'll explore how to build advanced Django custom tags.
Parsing Content
To build a paired cake tag, you lot'll demand to implement a grade that extends django.template.Node
. This class is responsible for rendering the tag. Django provides a utility for parsing the content between paired block tags, which it and then passes to your Node
class for rendering.
To demonstrate paired block tags, you'll implement a Markdown rendering tag. A library chosen mistune
has already done the heavy lifting for this. Use pip
to install mistune
:
1 $ python -m pip install mistune == 0.viii.4
At the time of writing this tutorial, mistune
has been undergoing an overhaul. The 2.0 version is in beta and is drastically different from the examples shown here. Make certain to install version 0.viii.iv, or exist prepared to suit the calls to the library.
Add the following code to dinosoar/dinofacts/templatetags/dinotags.py
:
109 # dinosoar/dinofacts/templatetags/dinotags.py 110 111 import mistune 112 113 @register . tag ( name = "markdown" ) 114 def do_markdown ( parser , token ): 115 nodelist = parser . parse (( "endmarkdown" ,)) 116 parser . delete_first_token () 117 render MarkdownNode ( nodelist ) 118 119 course MarkdownNode ( template . Node ): 120 def __init__ ( cocky , nodelist ): 121 self . nodelist = nodelist 122 123 def return ( self , context ): 124 content = self . nodelist . return ( context ) 125 result = mistune . markdown ( str ( content )) 126 render result
To build a cake tag, you need both a function and a class. Hither'southward how they work:
- Line 113 registers the
do_markdown()
function as a tag. Note that it uses theproper noun
argument to proper noun the tag and that it'due south using the.tag()
decorator, non.simple_tag()
. - Line 114 declares the tag. The arguments are different from a simple tag, taking a parser and a token. The parser is a reference to the template engine'southward parser that's parsing the template. You don't use the
token
argument in this case, and you'll explore information technology later. - Line 115 uses the
parser
object to continue parsing the template until it sees the finish tag, in this case{% endmarkdown %}
. - Line 116 calls
.delete_first_token()
to remove the opening tag. What you pass to theNode
class is simply what's between the opening and closing tags. - Line 117 instantiates the
Node
course that renders the template, and the template engine passes in the tokens from the parsed tag block. - Lines 119 to 121 declare and initialize the
Node
course. - Lines 123 to 126 render the content. For this tag, that involves using
mistune
to catechumen the tag block from Markdown into HTML. - Line 124 calls
.render()
on the contents of the cake. This ensures that any embedded template content gets handled and allows you to use values and filters inside your embedded Markdown. - Line 125 converts the rendered content into a string and then uses
mistune
to turn that into HTML. - Line 126 returns the result to be inserted into the rendered page. Annotation that the result is not autoescaped. Django expects that y'all know how to protect your users from HTML exploits if y'all're writing an advanced tag.
Now you'll test this new tag with some Markdown content. Create dinosoar/templates/steg.html
:
ane <!-- dinosoar/templates/steg.html --> 2 iii {% extends "base of operations.html" %} 4 {% load dinotags %} 5 6 {% block content %} 7 8 < h1 >Stegosaurus</ h1 > 9 ten < p > 11 {% markdown %} 12**Stegosaurus** ('roof-lizard') is a four-legged found eater from the thirteen*Late Jurassic*. It had: 14 15* Bony back plates 16* Big hindquarters 17* A tail tipped with spikes eighteen {% endmarkdown %} 19 </ p > xx 21 {% endblock content %}
Load http://127.0.0.1:8000/show_dino/steg/
into your browser to encounter the finished product:
The Markdown from within the block tag pair gets rendered as HTML by the template engine. 1 matter to keep in mind when playing with Markdown is that indentation is meaningful. If the tag and its contents had not been left-justified, the mistune
library would not have been able to catechumen it properly. This is like to using a <pre>
tag in HTML. Of a sudden, spacing matters.
Rendering Content
It's time to dive deeper into block tags. To meet how the parser deals with the contents of a tag, add the post-obit to dinotags.py
:
130 # dinosoar/dinofacts/templatetags/dinotags.py 131 132 @register . tag () 133 def shownodes ( parser , token ): 134 nodelist = parser . parse (( "endshownodes" ,)) 135 parser . delete_first_token () 136 return ShowNodesNode ( token , nodelist ) 137 138 grade ShowNodesNode ( template . Node ): 139 def __init__ ( cocky , token , nodelist ): 140 cocky . token = token 141 cocky . nodelist = nodelist 142 143 def render ( self , context ): 144 result = [ 145 "<ul><li>Token info:</li><ul>" , 146 ] 147 148 for part in self . token . split_contents (): 149 content = escape ( str ( function )) 150 result . append ( f "<li> { content } </li>" ) 151 152 result . suspend ( "</ul><li>Cake contents:</li><ul>" ) 153 for node in self . nodelist : 154 content = escape ( str ( node )) 155 effect . append ( f "<li> { content } </li>" ) 156 157 event . append ( "</ul>" ) 158 return "" . join ( consequence )
The contents of shownodes()
are quite similar to do_markdown()
. The only departure is that this time, the Node
course is going to take both token
and the parsed content as parameters. The .render()
method of ShowNodesNode
does the following:
- Lines 144 to 146 create a list that volition contain the results. The listing starts out with an HTML bulleted listing tag and a title.
- Lines 148 to 150 iterate through the contents of the token by calling
token.split_contents()
. This token contains the information from the opening tag, including its arguments. The parts of the token become added to the consequence as a bulleted sublist. - Lines 153 to 155 do something similar, but instead of operating on the token, they operate on the contents of the cake tag. Each particular in this bulleted sublist will exist one of the tokens parsed out of the tag block.
To see how tag parsing works, create dinosoar/templates/tri.html
and use {% shownodes %}
every bit follows:
1 <!-- dinosoar/templates/tri.html --> two 3 {% extends "base.html" %} 4 {% load dinotags %} v 6 {% block content %} 7 viii < h1 >Triceratops</ h1 > 9 ten < p > 11 Triceratops (3-horned face) is a plant eating quadruped from the 12 Late Cretaceous period. xiii 14 </ p > 15 xvi {% shownodes "pointy face up" "stubby tail" %} 17It has a big bony frill around its neck. A fully grown adult weighed 18 {{ weight }}. {% comment %} put more info here {% endcomment %} 19 {% endshownodes %} 20 21 {% endblock content %}
Your {% shownodes %}
tag contains some text, a Context
value, and a {% comment %}
tag. See what your debugging tag does past visiting http://127.0.0.i:8000/show_dino/tri/
:
This page shows you a bit more of what's going on when you write a block-level Django custom tag. The opening tag had ii arguments, "pointy face"
and "stubby tail"
, which can be accessed through token.split_contents()
. The parser divides the block content into five pieces:
- A
TextNode
object with the opening part of the sentence - A
VariableNode
object representing the valueweight
that would normally be rendered - Another
TextNode
object, ending the commencement sentence - A child,
CommentNode
- A
TextNode
object containing the final blank line
Usually in a block tag, you'd telephone call .render()
on whatever child contents, which would resolve weight
and remove the annotate in this case, simply y'all don't have to. Annihilation within your tag'due south block is nether your command.
Conclusion
Django is structured to separate business logic from presentation code through the utilize of views and templates. The template language is intentionally restrictive to aid enforce this separation.
The original developers of Django were attempting to separate the work of HTML designers from Python programmers. This doesn't hateful y'all're stuck with the built-in mechanisms, though. Django custom template tags and filters give you a valuable weapon in your tool breast.
In this tutorial, you learned:
- Where custom template tags and filters live
- How to write a custom filter
- When autoescaping will modify your results and how to deal with it
- How to write elementary tags with the
@simple_tag
and@inclusion_tag
decorators - About avant-garde custom tags and parsing the contents of a cake tag
For more data about Django, get to the Django project home folio. For more information about tags and filters, check out the Django documentation, particularly the sections on born template tags and filters and custom template tags and filters.
There'south also plenty more to larn about Django on the Django for Web Development Learning Path. Dig in, chow down, have a large bite, and go a Djangosaurus Male monarch.
How To Register Tags Django,
Source: https://realpython.com/django-template-custom-tags-filters/
Posted by: howerappress.blogspot.com
0 Response to "How To Register Tags Django"
Post a Comment