A Python dictionary is a mapping between known keys and variable values. A Context
is similar to a dictionary, but a Context
provides additional functionality, as covered in Chapter 8, Advanced Templates.
Variable names must begin with a letter (A-Z or a-z) and may contain more letters, digits, underscores, and dots. (Dots are a special case we'll get to in a moment.) Variable names are case sensitive. Here's an example of template compilation and rendering, using a template similar to the example in the beginning of this chapter:
>>> from django.template import Template, Context >>> raw_template = """<p>Dear {{ person_name }},</p> ... ... <p>Thanks for placing an order from {{ company }}. It's scheduled to ... ship on {{ ship_date|date:"F j, Y" }}.</p> ... ... {% if ordered_warranty %} ... <p>Your warranty information will be included in the packaging.</p> ... {% else %} ... <p>You didn't order a warranty, so you're on your own when ... the products inevitably stop working.</p> ... {% endif %} ... ... <p>Sincerely,<br />{{ company }}</p>""" >>> t = Template(raw_template) >>> import datetime >>> c = Context({'person_name': 'John Smith', ... 'company': 'Outdoor Equipment', ... 'ship_date': datetime.date(2015, 7, 2), ... 'ordered_warranty': False}) >>> t.render(c) u"<p>Dear John Smith,</p> <p>Thanks for placing an order from Outdoor Equipment. It's scheduled to ship on July 2, 2015.</p> <p>You didn't order a warranty, so you're on your own when the products inevitably stop working.</p> <p>Sincerely,<br />Outdoor Equipment </p>"
Template
and Context
, which both live in the module django.template
.raw_template
. Note that we use triple quote marks to designate the string, because it wraps over multiple lines; in contrast, strings within single quote marks cannot be wrapped over multiple lines.t
, by passing raw_template
to the Template
class constructor.datetime
module from Python's standard library, because we'll need it in the following statement.Context
object, c
. The Context
constructor takes a Python dictionary, which maps variable names to values. Here, for example, we specify that the person_name
is "John Smith
", company
is "Outdoor Equipment
", and so forth.render()
method on our template object, passing it the context. This returns the rendered template-that is, it replaces template variables with the actual values of the variables, and it executes any template tags.Note that the You didn't order a warranty paragraph was displayed because the ordered_warranty
variable evaluated to False
. Also note the date, July 2, 2015
, which is displayed according to the format string "F j, Y
". (We'll explain format strings for the date
filter in a little while.)
If you're new to Python, you may wonder why this output includes newline characters ("
") rather than displaying the line breaks. That's happening because of a subtlety in the Python interactive interpreter: the call to t.render(c)
returns a string, and by default the interactive interpreter displays the representation of the string, rather than the printed value of the string. If you want to see the string with line breaks displayed as true line breaks rather than "
" characters, use the print function: print (t.render(c))
.
Those are the fundamentals of using the Django template system: just write a template string, create a Template
object, create a Context
, and call the render()
method.
Once you have a Template
object, you can render multiple contexts through it. For example:
>>> from django.template import Template, Context >>> t = Template('Hello, {{ name }}') >>> print (t.render(Context({'name': 'John'}))) Hello, John >>> print (t.render(Context({'name': 'Julie'}))) Hello, Julie >>> print (t.render(Context({'name': 'Pat'}))) Hello, Pat
Whenever you're using the same template source to render multiple contexts like this, it's more efficient to create the Template
object once, and then call render()
on it multiple times:
# Bad for name in ('John', 'Julie', 'Pat'): t = Template('Hello, {{ name }}') print (t.render(Context({'name': name}))) # Good t = Template('Hello, {{ name }}') for name in ('John', 'Julie', 'Pat'): print (t.render(Context({'name': name})))
Django's template parsing is quite fast. Behind the scenes, most of the parsing happens via a call to a single regular expression. This is in stark contrast to XML-based template engines, which incur the overhead of an XML parser and tend to be orders of magnitude slower than Django's template rendering engine.
In the examples so far, we've passed simple values in the contexts-mostly strings, plus a datetime.date
example. However, the template system elegantly handles more complex data structures, such as lists, dictionaries, and custom objects. The key to traversing complex data structures in Django templates is the dot character (".
").
Use a dot to access dictionary keys, attributes, methods, or indices of an object. This is best illustrated with a few examples. For instance, suppose you're passing a Python dictionary to a template. To access the values of that dictionary by dictionary key, use a dot:
>>> from django.template import Template, Context >>> person = {'name': 'Sally', 'age': '43'} >>> t = Template('{{ person.name }} is {{ person.age }} years old.') >>> c = Context({'person': person}) >>> t.render(c) 'Sally is 43 years old.'
Similarly, dots also allow access to object attributes. For example, a Python datetime.date
object has year
, month
, and day
attributes, and you can use a dot to access those attributes in a Django template:
>>> from django.template import Template, Context >>> import datetime >>> d = datetime.date(1993, 5, 2) >>> d.year 1993 >>> d.month 5 >>> d.day 2 >>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.') >>> c = Context({'date': d}) >>> t.render(c) 'The month is 5 and the year is 1993.'
This example uses a custom class, demonstrating that variable dots also allow attribute access on arbitrary objects:
>>> from django.template import Template, Context >>> class Person(object): ... def __init__(self, first_name, last_name): ... self.first_name, self.last_name = first_name, last_name >>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.') >>> c = Context({'person': Person('John', 'Smith')}) >>> t.render(c) 'Hello, John Smith.'
Dots can also refer to methods of objects. For example, each Python string has the methods upper()
and isdigit()
, and you can call those in Django templates using the same dot syntax:
>>> from django.template import Template, Context >>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}') >>> t.render(Context({'var': 'hello'})) 'hello -- HELLO -- False' >>> t.render(Context({'var': '123'})) '123 -- 123 -- True'
Note that you do not include parentheses in the method calls. Also, it's not possible to pass arguments to the methods; you can only call methods that have no required arguments. (We explain this philosophy later in this chapter.) Finally, dots are also used to access list indices, for example:
>>> from django.template import Template, Context >>> t = Template('Item 2 is {{ items.2 }}.') >>> c = Context({'items': ['apples', 'bananas', 'carrots']}) >>> t.render(c) 'Item 2 is carrots.'
Negative list indices are not allowed. For example, the template variable
{{ items.-1 }}
would cause a TemplateSyntaxError
.
Dot lookups can be summarized like this: when the template system encounters a dot in a variable name, it tries the following lookups, in this order:
foo["bar"]
)foo.bar
)foo.bar()
)foo[2]
)The system uses the first lookup type that works. It's short-circuit logic. Dot lookups can be nested multiple levels deep. For instance, the following example uses {{ person.name.upper }}
, which translates into a dictionary lookup (person['name']
) and then a method call (upper()
):
>>> from django.template import Template, Context >>> person = {'name': 'Sally', 'age': '43'} >>> t = Template('{{ person.name.upper }} is {{ person.age }} years old.') >>> c = Context({'person': person}) >>> t.render(c) 'SALLY is 43 years old.'
Method calls are slightly more complex than the other lookup types. Here are some things to keep in mind:
silent_variable_failure
whose value is True
. If the exception does have a silent_variable_failure
attribute, the variable will render as the value of the engine's string_if_invalid
configuration option (an empty string, by default). For example:>>> t = Template("My name is {{ person.first_name }}.") >>> class PersonClass3: ... def first_name(self): ... raise AssertionError("foo") >>> p = PersonClass3() >>> t.render(Context({"person": p})) Traceback (most recent call last): ... AssertionError: foo >>> class SilentAssertionError(Exception): ... silent_variable_failure = True >>> class PersonClass4: ... def first_name(self): ... raise SilentAssertionError >>> p = PersonClass4() >>> t.render(Context({"person": p})) 'My name is .'
BankAccount
object that has a delete()
method. If a template includes something like {{ account.delete }}
, where account
is a BankAccount
object, the object would be deleted when the template is rendered! To prevent this, set the function attribute alters_data
on the method:def delete(self): # Delete the account delete.alters_data = True
{{ account.delete }}
and the delete()
method has the alters_data=True
, then the delete()
method will not be executed when the template is rendered, the engine will instead replace the variable with string_if_invalid
.delete()
and save()
methods on Django model objects get alters_data=true
set automatically.Generally, if a variable doesn't exist, the template system inserts the value of the engine's string_if_invalid
configuration option, which is an empty string by default. For example:
>>> from django.template import Template, Context >>> t = Template('Your name is {{ name }}.') >>> t.render(Context()) 'Your name is .' >>> t.render(Context({'var': 'hello'})) 'Your name is .' >>> t.render(Context({'NAME': 'hello'})) 'Your name is .' >>> t.render(Context({'Name': 'hello'})) 'Your name is .'
This behavior is better than raising an exception because it's intended to be resilient to human error. In this case, all of the lookups failed because variable names have the wrong case or name. In the real world, it's unacceptable for a web site to become inaccessible due to a small template syntax error.