二分春色到穷阎,儿女祈翁出滞淹。幽蛰夜惊雷奋地,小窗朝爽日筛帘。
惠风全解墨池冻,清昼胜翻云笈签。亲友莫嗔情话少,向来屏息似龟蟾。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
作者:小亲姐姐
来源链接:http://www.qbaobei.com/jiaoyu/407464.html
来源:亲亲宝贝
著作权归作者所有。
In the previous chapter, you may have noticed something peculiar in how we returned the text in our example views. Namely, the HTML was hard-coded directly in our Python code, like this:(在前面的章节,你可能已经注意到我们在例子试图中返回的文本方式很奇怪。也就是说,HTML被直接硬编码到我们的Python代码中就像这样:)
def current_datetime(request): now = datetime.datetime.now() html = "It is now %s." % now return HttpResponse(html)
Although this technique was convenient for the CCC of explaining how views work, it’s not a good idea to hard-code HTML directly into your views. Here’s why:(尽管这种技术便于解释这种视图是如何工作的,但将你的HTML硬编码到你的试图里面却不是一个好主意,这里是为什么:)
- Any change to the design of the page requires a change to the Python code. The design of a site tends to change far more frequently than the underlying Python code, so it would be convenient if the design could change without needing to modify the Python code.(任何对页面进行的改变都必须改变Python代码。站点设计的修改往往比底层的Python代码的修改要频繁的多,因此如果可以在不进行Python代码修改的情况下改变设计,那将会方便的多。)
- This is only a very simple example. A common webpage template has hundreds of lines of HTML and scripts. Untangling and troubleshooting program code from this mess is a nightmare (cough-PHP-cough).
- Writing Python code and designing HTML are two different disciplines, and most professional Web development environments split these responsibilities between separate people (or even separate departments). Designers and HTML/CSS coders shouldn’t be required to edit Python code to get their job done.
- It’s most efficient if programmers can work on Python code and designers can work on templates at the same time, rather than one person waiting for the other to finish editing a single file that contains both Python and HTML.
For these reasons, it’s much cleaner and more maintainable to separate the design of the page from the Python code itself. We can do this with Django’s template system, which we discuss in this chapter.
Template System Basics
A Django template is a string of text that is intended to separate the presentation of a document from its data. A template defines placeholders and various bits of basic logic (template tags) that regulate how the document should be displayed. Usually, templates are used for producing HTML, but Django templates are equally capable of generating any text-based format.
Philosophy behind Django’s templatesIf you have a background in programming, or if you’re used to languages which mix programming code directly into HTML, you’ll want to bear in mind that the Django template system is not simply Python embedded into HTML. This is by design: the template system is meant to express presentation, not program logic.
Let’s start with a simple example template. This Django template describes an HTML page that thanks a person for placing an order with a company. Think of it as a form letter:
<html>
<head>
<title>Ordering notice</title>
</head>
<body>
<h1>Ordering notice</h1>
<p>Dear {{ person_name }},</p>
<p>Thanks for placing an order from {{ company }}. It's scheduled to ship on {{ s
hip_date|date:"F j, Y" }}.</p>
<p>Here are the items you've ordered:</p>
<ul>
{% for item in item_list %}
<li>{{ item }}</li>{% endfor %}
</ul>
{% 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>
</body>
</html>
This template is basic HTML with some variables and template tags thrown in. Let’s step through it:
- Any text surrounded by a pair of braces (e.g.,
{{ person_name }}
) is a variable. This means “insert the value of the variable with the given name.” How do we specify the values of the variables? We’ll get to that in a moment. - Any text that’s surrounded by curly braces and percent signs (e.g.
{% if ordered_warranty %}
) is a template tag.
The definition of a tag is quite broad: a tag just tells the template system to “do something”. - This example template contains a
for
tag ({% for item in item_list %}
) and anif
tag ({% if ordered_warranty %}
). Afor
tag works very much like afor
statement in Python, letting you loop over each item in a sequence.Anif
tag, as you may expect, acts as a logical “if” statement.
In this particular case, the tag checks whether the value of theordered_warranty
variable evaluates toTrue
. If it does, the template system will display everything between the{% if ordered_warranty %}
and{% else %}
. If not, the template system will display everything between{% else %}
and{% endif %}
. Note that the{% else %}
is optional. - Finally, the second paragraph of this template contains an example of a filter, which is the most convenient way to alter the formatting of a variable. In this example,
{{ ship_date|date:"F j, Y" }}
, we’re passing theship_date
variable to thedate
filter, giving thedate
filter the argument"F j, Y"
. Thedate
filter formats dates in a given format, as specified by that argument. Filters are attached using a pipe character (|
), as a reference to Unix pipes.
Each Django template has access to several built-in tags and filters, many of which are discussed in the sections that follow. Appendix E contains the full list of tags and filters, and it’s a good idea to familiarize yourself with that list so you know what’s possible. It’s also possible to create your own filters and tags; we’ll cover that in Chapter 8.
Using the Template System
A Django project can be configured with one or several template
engines (or even zero if you don’t use templates). Django ships with a
built-in backend for its own template system – the Django Template Language (DTL). Django 1.8 also includes support for the popular alternative Jinja2.
If you don’t have a pressing reason to choose another backend, you
should use the DTL – especially if you’re writing a pluggable
application and you intend to distribute templates. Django’s contrib
apps that include templates, like django.contrib.admin
, use the DTL. All of the examples in this chapter will use the DTL.
For more advanced template topics, including configuring third-party
template engines, see Chapter 8. Before we go about implementing Django
templates in your view, lets first dig inside the DTL a little so you
can see how it works.
Here is the most basic way you can use Django’s template system in Python code:
- Create a
Template
object by providing the raw template code as a string. - Call the
render()
method of theTemplate
object with a given set of variables (the context). This returns a fully rendered template as a string, with all of the variables and template tags evaluated according to the context. In code, here’s what that looks like:
>>> from django import template >>> t = template.Template('My name is {{ name }}.') >>> c = template.Context({'name': 'Nige'}) >>> print (t.render(c)) My name is Nige. >>> c = template.Context({'name': 'Barry'}) >>> print (t.render(c)) My name is Barry.
The following sections describe each step in much more detail.
Creating Template Objects
The easiest way to create a Template
object is to instantiate it directly. The Template
class lives in the django.template
module, and the constructor takes one argument, the raw template code. Let’s dip into the Python interactive interpreter to see how this works in code. From the mysite
project directory you created in Chapter 1, type python manage.py shell
to start the interactive interpreter.
Let’s go through some template system basics:
>>> from django.template import Template
>>> t = Template('My name is {{ name }}.')
>>> print (t)
If you’re following along interactively, you’ll see something like this:
<django.template.base.Template object at 0x030396B0>
That 0x030396B0
will be different every time, and it isn’t relevant; it’s a Python thing (the Python “identity” of the Template
object, if you must know).
When you create a Template
object, the template system compiles the raw template code into an internal, optimized form, ready for rendering. But if your template code includes any syntax errors, the call to Template()
will cause a TemplateSyntaxError
exception:
>>> from django.template import Template
>>> t = Template('{% notatag %}')
Traceback (most recent call last):
File "", line 1, in ?
...
django.template.base.TemplateSyntaxError: Invalid block tag: 'notatag'
The term “block tag” here refers to {% notatag %}
. “Block tag” and “template tag” are synonymous. The system raises a TemplateSyntaxError
exception for any of the following cases:
- Invalid tags
- Invalid arguments to valid tags
- Invalid filters
- Invalid arguments to valid filters
- Invalid template syntax
- Unclosed tags (for tags that require closing tags)
Rendering a Template
Once you have a Template
object, you can pass it data by giving it a context. A context is simply a set of template variable names and their associated values.
A template uses this to populate its variables and evaluate its tags. A context is represented in Django by the Context
class, which lives in the django.template
module.
Its constructor takes one optional argument: a dictionary mapping variable names to variable values. Call the Template
object’s render()
method with the context to “fill” the template:
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
'My name is Stephane.'
A special Python promptIf you’ve used Python before, you may be wondering why we’re running python manage.py shell
instead of just python
(or python3
). Both commands will start the interactive interpreter, but the manage.py shell
command has one key difference: before starting the interpreter, it tells Django which settings file to use.Many parts of Django, including the template system, rely on your settings, and you won’t be able to use them unless the framework knows which settings to use.If you’re curious, here’s how it works behind the scenes. Django looks for an environment variable called DJANGO_SETTINGS_MODULE
, which should be set to the import path of your settings.py
. For example, DJANGO_SETTINGS_MODULE
might be set to 'mysite.settings'
, assuming mysite
is on your Python path.When you run python manage.py shell
, the command takes care of setting DJANGO_SETTINGS_MODULE
for you. You will need to use python manage.py shell
in these examples, or Django will throw an exception.
Dictionaries and Contexts
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.
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)
"<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>"
- First, we import the classes
Template
andContext
, which both live in the moduledjango.template
. - We save the raw text of our template into the variable
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. - Next, we create a template object,
t
, by passingraw_template
to theTemplate
class constructor. - We import the
datetime
module from Python’s standard library, because we’ll need it in the following statement. - Then, we create a
Context
object,c
. TheContext
constructor takes a Python dictionary, which maps variable names to values. Here, for example, we specify that theperson_name
is “John Smith
”,company
is “Outdoor Equipment
”, and so forth. - Finally, we call the
render()
method on our template object, passing it the context. This returns the rendered template – i.e., 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 theordered_warranty
variable evaluated toFalse
. Also note the date,July 2, 2015
, which is displayed according to the format string “F j, Y
”. (We’ll explain format strings for thedate
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.
Multiple Contexts, Same Template
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.
Context Variable Lookup
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. (I 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
.
Python ListsA reminder: Python lists have 0-based indices. The first item is at index 0, the second is at index 1, and so on.
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:
- Dictionary lookup (e.g.,
foo["bar"]
) - Attribute lookup (e.g.,
foo.bar
) - Method call (e.g.,
foo.bar()
) - List-index lookup (e.g.,
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 Call Behavior
Method calls are slightly more complex than the other lookup types. Here are some things to keep in mind:
- If, during the method lookup, a method raises an exception, the exception will be propagated, unless the exception has an attribute
silent_variable_failure
whose value isTrue
. If the exception does have asilent_variable_failure
attribute, the variable will render as the value of the engine’sstring_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 .'
- A method call will only work if the method has no required arguments. Otherwise, the system will move to the next lookup type (list-index lookup).
- By design, Django intentionally limits the amount of logic processing available in the template, so it’s not possible to pass arguments to method calls accessed from within templates. Data should be calculated in views and then passed to templates for display.
- Obviously, some methods have side effects, and it would be foolish at best, and possibly even a security hole, to allow the template system to access them.
- Say, for instance, you have a
BankAccount
object that has adelete()
method. If a template includes something like{{ account.delete }}
, whereaccount
is aBankAccount
object, the object would be deleted when the template is rendered! To prevent this, set the function attributealters_data
on the method:
def delete(self): # Delete the account delete.alters_data = True
The template system won’t execute any method marked in this way. Continuing the above example, if a template includes
{{ account.delete }}
and thedelete()
method has thealters_data=True
, then thedelete()
method will not be executed when the template is rendered, the engine will instead replace the variable withstring_if_invalid
.NOTE: The dynamically-generated
delete()
andsave()
methods on Django model objects getalters_data=true
set automatically.
How Invalid Variables Are Handled
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 behaviour 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.