Django框架模板基础
模板使用
模板基本由两个部分组成,一是HTML代码,二是逻辑控制代码。
逻辑控制的实现又基本由三个部分组成:
1. 变量的使用
{{ person_name }} #使用双大括号来引用变量
2. tag的使用
{% if ordered_warranty %} #使用大括号和百分号的组成来表示使用Django提供的
template tag
{% for item in item_list %}
- {{ item }}
- {% endfor %}
- 3. filter的使用
- {{ ship_date|date:"F j, Y" }},ship_date变量传给data过滤器,data过滤器通过使用
- "F j, Y"这几个参数来格式化日期数据。"|"代表类似Unix命令中的管道操作。
- Template system不仅仅可以和view进行合作,也可以自己独立使用。
- 最基本的使用方法是:
- 1. 使用模板代码字符串作为参数,创建一个Template类
- 2. 创建模板代码中所需要的上下文Context对象,包含所需要的各个引用参数的值
- 3. 调用Template.render()方法,把template渲染成一个完整的字符串。
- >>> from django import template
- >>> t = template.Template('My name is {{ name }}.')
- >>> c = template.Context({'name': 'Adrian'})
- >>> print t.render(c)
- >>> My name is Adrian.
- 还可以在template代码中使用dict索引,然后在context传入所需要的dict
- >>> 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)
- u'Sally is 43 years old.'
- 还可以使用函数,不过只能使用无参数的函数
- >>> from django.template import Template, Context
- >>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
- >>> t.render(Context({'var': 'hello'}))
- u'hello -- HELLO -- False'
- 还可以使用列表索引,但是item.-1是不被允许的
- >>> from django.template import Template, Context
- >>> t = Template('Item 2 is {{ items.2 }}.')
- >>> c = Context({'items': ['apples', 'bananas', 'carrots']})
- >>> t.render(c)
- u'Item 2 is carrots.'
- 以上的使用方法被称为Dot Lookup方法。
- 使用dot lookup的访问函数功能时,需要注意的问题:
- 1. 当在模板代码中执行的函数抛出异常时,会一直向上层传播,除非这个异常中有一个参数
- silent_variable_failure=True; 这样的话,这个出错的函数信息就会被渲染成空字符串。
- >>> class SilentAssertionError(AssertionError):
- ... silent_variable_failure = True
- >>> class PersonClass4:
- ... def first_name(self):
- ... raise SilentAssertionError
- >>> p = PersonClass4()
- >>> t.render(Context({"person": p}))
- u'My name is .'
- 2. 很明显,调用函数会产生一些不好的结果,安全漏洞之类的,如果你有一个BankAccout,
- 然后在模板中写成{{ account.delete }}, 这样在渲染的时候,你的账号就被删除了。。。。
- 所以要在修改一下你的delete函数
- def delete(self):
- # Delete the account
- delete.alters_data = True#缩进没有问题,把delete看成一个对象,设置它的alters_data属性。
- 这样在渲染的时候,就会变成failed silent。
- 当在渲染的时候,简单的key值没有找到时,会failed silent,变成空字符串,而不是大动干戈的
- 报错。
- >>> from django.template import Template, Context
- >>> t = Template('Your name is {{ name }}.')
- >>> t.render(Context())
- u'Your name is .'
- >>> t.render(Context({'var': 'hello'}))
- u'Your name is .'
- Context对象也可以进行增删改值的操作。
- >>> from django.template import Context
- >>> c = Context({"foo": "bar"})
- >>> c['foo']
- 'bar'
- >>> c['newvariable'] = 'hello'
- >>> del c['foo']
- >>> c['foo']
- 使用python manage.py shell启动python交互式命令行窗口与一般直接启动python自带的
- 交互式命令行窗口的区别是前者会通过找一个DJANGO_SETTINGS_MODULE环境变量,
- 告诉Django导入settings.py的配置信息。
- 基本的tag和filter的用法
- tag:
- {% if %}的使用
- 可以使用and, or, not来组织你的逻辑。但不允许and和or同时出现的条件语句中。
- 没有{% elif %}这样的用法,只能用嵌套来实现多重if语句。
- {% if athlete_list %}
Here are the athletes: {{ athlete_list }}.
{% else %}
No athletes are available.
{% if not coach_list %}
Here are the coaches: {{ coach_list }}.
{% endif %}
{% endif %}
{% for %} 的使用
用来循环一个list,还可以使用resersed关键字来进行倒序遍历,一般可以用if语句来先
判断一下列表是否为空,再进行遍历;还可以使用empty关键字来进行为空时候的跳转。
{% for athlete in athlete_list resersed %}
{{ athlete.name }}
{% empty %}
There are no athletes. Only computer programmers.
{% endfor %}
for tag还提供了一些内置参数来提供模板循环的信息。
1. forloop.counter 当前循环计数,从1开始
{% for item in todo_list %}
{{ forloop.counter }}: {{ item }}
{% endfor %}
2. forloop.counter0 当前循环计数,从0开始,标准索引方式
3. forloop.revcounter 当前循环的倒数计数,从列表长度开始
4. forloop.revcounter0 当前循环的倒数计数,从列表长度减1开始,标准
5. forloop.first bool值,判断是不是循环的第一个元素
6. forloop.last 同上,判断是不是循环的最后一个元素
7. forloop.parentloop 用在嵌套循环中,得到parent循环的引用,然后可以使用以上的参数
{% for country in countries %}
{% for city in country.city_list %}
{% endfor %}
Country #{{ forloop.parentloop.counter }} | City #{{ forloop.counter }} | {{ city }} |
{% endfor %}
ifequal和ifnotequal,一看就是直接比较值的tag,需要两个参数,用法比较有限,
只限于字符串,整数,小数的比较,什么列表,字典,元组不支持。
{% ifequal user currentuser %}
Welcome!
{% ifequal section "community" %}
Community
{% endifequal %}
{% endifequal %}
{# #},模板中注释的用法,只能用在一行
如果要使用多行注释,要使用{% comment %}
{# This is a comment #}
{% comment %}
This is a
multi-line comment.
{% endcomment %}
filter:
filter用于变量在显示之前的一些简单的处理。使用类似管道的操作符"|",也可以进行链式操作
{{ name|lower }}
{{ my_list|first|upper }}
{{ bio|truncatewords:"30" }}
介绍几个重要的filter:
addslashes :给任何的反斜线,单引号,双引号,再加一个反斜线。在文本中含有
javascript
字符串的时候有用。
date :用来对data和datatime对象的字符串信息进行格式化。
{{ pub_date|date:"F j, Y" }}
length :返回变量的长度。
在view中使用template:
首先在settings.py中配置模板文件的路径。
TEMPLATE_DIRS = (
'/home/django/mysite/templates',
)
记住一个路径的时候要使用逗号,这样是来区分是一个tuple还是一个block expression
也可以在设置的时候使用python文件路径操作代码:
import os.path
TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
)
然后,可以在view中使用模板
from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
import datetime
def current_datetime(
request
):
now = datetime.datetime.now()
t = get_template('current_datetime.html')
html = t.render(Context({'current_date': now}))
return HttpResponse(html)
大多数情况下,你会使用一种shortcut方法,render_to_response()去完成以上的工作。
from django.shortcuts import render_to_response
import datetime
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})
locals()的小技巧
如果你有很多变量要传给render,一个一个构造dict元素很麻烦。直接把变量名改成模板中所需要的变量名,
再使用locasl()函数,轻松搞定
def current_datetime(request):
current_date = datetime.datetime.now()
return render_to_response('current_datetime.html', locals())
locals()会返回局部空间中变量信息的dict,直接可以传给render,但有一点需要注意,它会返回把有的局部变量
信息,有一些可能不需要用到,如request变量。
{%
include
%}的使用
{% include 'nav.html' %},用来引入其它模板的内容,减少重复的模板代码
{% include template_name %},还可以使用变量名
如果include的模板文件没有找到,当DEBUG为真时,会报错TemplateDoesNotExist,当为假时,页面那一块为
空白。
诚然,include可以有效减少模板的重复代码。但一种更优雅的方式是:
template inheritance.
首先,创建base.html
{% block title %}{% endblock %}
My helpful timestamp site
{% block content %}{% endblock %}
{% block footer %}
Thanks for visiting my site.
{% endblock %}
我们使用一个新的tag,{% block %}用来告诉template engine,这个部分会被子模板
来实现。如果子模板没有实现这些部分,就会默认使用父模板的代码。
再看看,子模板要怎么写:
{% extends "base.html" %}
{% block title %}The current time{% endblock %}
{% block content %}
It is now {{ current_date }}.
{% endblock %}
只需要先使用{% extends %}继承父模板,再把相应需要实现的部分写上所需要的内容。
{% extends template_name %}也可以使用变量名来实现动态。
模板继承的三层继承策略:
1. 创建一个base.html,用来设置外观
2. 为网站的每一个部分,创建base_SECTION.html,比如base_phote.html, base_forum.html
3. 为每一个页面创建自己的模板。
Django框架模板高级进阶
1. RequestContext函数和context处理器
首先,我们回顾模板的视图函数如何书写:
from django.shortcuts import render_to_response
def diary(request):
return render_to_response('diary.html', {'name': 'qiqi'})
为了说明方便,我们同时给出另一种写法:
from django.http import HttpResponse from django.template import loader, Context def diary(request): t = loader.get_template('diary.html') c = Context({'name': 'qiqi'}) return HttpResponse(t.render(c))
或许还是有同学看不懂,那我就再给出第三种最笨的等价写法,大家大可略过直接往后看:
from django.http import HttpResponse from django.template import Template, Context def diary(request): tin = open('./templates/diary.html') html = tin.read() tin.close() inf = {'name': 'qiqi'} t = Template(html) c = Context(inf) return HttpResponse(t.render(c))
在上述代码中,我们是用Context函数进行渲染的。
而与django.template.Context函数差不多的,还有一个django.template.RequestContext函数,它默认在模板中加了一些变量,比如HttpRequest对象、当前登录用户的相关信息……
来看下面这段代码:
from django.http import HttpResponse from django.template import loader, Context def view_1(request): # ... t = loader.get_template('template1.html') c = Context({ 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'], 'message': 'I am view 1.' }) return HttpResponse(t.render(c)) def view_2(request): # ... t = loader.get_template('template2.html') c = Context({ 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'], 'message': 'I am the second view.' }) return HttpResponse(t.render(c))
其中app、user、ip_address这三个变量,可能需要在很多模板的渲染中重复写入,很麻烦。而利用RequestContext函数,我们便可以将之简化为这样:
from django.http import HttpResponse from django.template import loader, RequestContext def custom_proc(request): return { 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'] } def view_1(request): # ... t = loader.get_template('template1.html') c = RequestContext(request, {'message': 'I am view 1.'}, [custom_proc]) return HttpResponse(t.render(c)) def view_2(request): # ... t = loader.get_template('template2.html') c = RequestContext(request, {'message': 'I am the second view.'}, [custom_proc]) return HttpResponse(t.render(c))
其中,我们写的函数custom_proc,便称为context处理器。用术语来说,我们利用RequestContext函数和context处理器,简化了使用模板的代码。
而在此基础上使用render_to_response,我们还可以将之简化成:
from django.shortcuts import render_to_response from django.template import RequestContext def custom_proc(request): return { 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'] } def view_1(request): # ... return render_to_response('template1.html', {'message': 'I am view 1.'}, context_instance=RequestContext(request, [custom_proc])) def view_2(request): # ... return render_to_response('template2.html', {'message': 'I am the second view.'}, context_instance=RequestContext(request, [custom_proc]))
此时,还是有一点美中不足的——渲染语句中,每次都需要手动为processors赋值。
Django提供了对全局context处理器的支持——可以让你在全局变量中列出一些context处理器,Django在你每次调用RequestContext函数时都会自动加入这些处理器的。这个全局变量在TEMPLATES参数中可以找到,其默认值是:
# TEMPLATES参数中,'OPTIONS'字典中的,'context_processors'列表 [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ]
这四个值不多说,可以去查书和手册。此时,我们可以进一步简化代码如下:
from django.shortcuts import render_to_response from django.template import RequestContext def view_1(request): # ... return render_to_response('template1.html', {'message': 'I am view 1.'}, context_instance=RequestContext(request)) def view_2(request): # ... return render_to_response('template2.html', {'message': 'I am the second view.'}, context_instance=RequestContext(request))
至于context处理器,则应当写在单独的一份代码中,推荐命名为context_processors.py。
只要把这份代码放在Python搜索路径中,Django就可以找到。推荐把它放在project或者app的目录下。
# context_processors.py def custom_proc(request): return { 'app': 'My app', 'user': request.user, 'ip_address': request.META['REMOTE_ADDR'] }
最后,修改全局变量即可:
[
'context_processors.custom_proc',
]
2. html自动转义
从模板生成html时,总会有一个风险——变量包含了会影响html的字符。
书中举了小例子,这里不加赘述,直接说解决方法:
1. 确保每个不被信任的变量都用escape过滤器过滤一遍,把潜在的有害html字符转换为无害的。这是初几年Django的处理策略。
2. 利用Django的html自动转义。下面介绍这种方法。
只要你使用Django模板,那么变量标签中的以下五个字符都会被自动转义:
原字符 转义结果
< <
> >
' '
" "
& &
当然,有时候你会自己手动用模板变量写一段html代码,那这时候你就需要关闭自动转义了,关闭方法如下:
1. 变量级别,用safe过滤器: This will not be escaped: {{ data|safe }}
2. 模板级别,用autoescape标签: {% autoescape off %} ... {% endautoescape %}
这个标签有两种状态:off、on。
这个标签是可以嵌套的,例如你可以在一个off的标签内嵌套on的标签。
当你使用模板继承的时候,显而易见,这个标签依旧会持续生效的。
3. 站点级别,这个书中只说有方法,却没写,暂时存疑。
注意:
1. 过滤多次和过滤一次效果完全一样。
2. 模板中的字符串常量(例如过滤器中包含的常量)会逃过自动转义,而变量中的字符串则不会。
{{ data|default:"3 < 2" }}
{{ data|default:"3 < 2" }} <-- Bad! Don't do this.
存疑:
1. escape过滤器是什么?根据文档中所写,好像是专指autoescape标签?
3. Django如何加载模板
3.1 什么是Engine
Engine,简单来说就是关于模板的一份设置,具体定义这里暂不介绍,大家可以先去文章开头所给的文档中查看,以后有时间会补上。
平时加载模板,使用的就是Django中默认的一个Engine对象,其内容就是咱们在settings.py中那个TEMPLATES参数。因此,你用默认的方式加载模板,也就等同于你用Django中这个默认的Engine对象来加载模板。这个对象是: django.template.engines['django']
而如果你想要实例化另一个Engine对象,则需要用这个定义: django.template.Engine()
3.2 加载模板的语句
Django加载模板的语句有三种:
1. Engine.from_string(template_code)
按照Engine对象的设置,编译所给代码生成模板,返回一个Template对象。
# 方法一,使用默认的Engine
from django.template import engines
template = engines['django'].from_string(template_code)
# 方法二,使用一个空的Engine(没有context_processors之类的东西)
from django.template import Engine
template = Engine().from_string(template_code)
2. Engine.get_template(template_name)
按照Engine对象的设置,根据所给名称找到模板,在内部进行编译,最后返回一个Template对象。
如果模板不存在,则返回一个TemplateDoesNotExist的异常。
3. Engine.select_template(self, template_name_list)
按照Engine对象的设置,根据所给列表中的名称,顺次寻找模板,把找到的第一个同样处理,返回一个Template对象。
如果全都不存在,则返回一个TemplateDoesNoeExist的异常。
3.3 模板加载器
我们一直说在加载模板,但是TEMPLATES参数中却并没有加载器的设置,此时我们所用的一直是Django中默认的加载器。
下面,我们就一一介绍Django中的模板加载器:
1. django.template.loaders.filesystem.Loader(默认)
从文件系统中加载模板。
路径:TEMPLATE参数中'DIRS'列表。
2. django.template.loaders.app_directories.Loader
从文件系统中加载模板。
路径:各app,即INSTALLED_APPS参数每个app目录下的templates文件夹。
开启方式:将TEMPLATES参数中'APP_DIRS'设置为True。
3. django.template.loaders.eggs.Loader
从Python egg中加载模板。
路径:各app。
开启方式:写出类似代码(未经测试,仅供参考)——
TEMPLATES = [{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'OPTIONS': { 'loaders': [ ('django.template.loaders.eggs.Loader'), ], }, }]
4. django.template.loaders.cached.Loader
加载模板时,第一次调用各加载器加载模板并存入缓存,以后则从缓存中直接加载模板。
路径:取决于调用的各加载器。
注意:实际加载的各模板应该保证其结点(Node)是进程安全(thread-safe)的,详见文档。
开启方式:写出类似代码(源自文档)——
TEMPLATES = [{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'OPTIONS': { 'loaders': [ ('django.template.loaders.cached.Loader', [ 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', ]), ], }, }]
5. django.template.loaders.locmem.Loader
从Python字典中加载模板,常用于测试。
存疑:实际效果未测试过,不是很能理解。
开启方式:写出类似代码(源自文档)——
TEMPLATES = [{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'OPTIONS': { 'loaders': [ ('django.template.loaders.locmem.Loader', { 'index.html': 'content here', }), ], }, }]
实际上,书和文档中还提到了如何写自己的模板、模板中用于debug的两个类、如何写独立模型下的模板……
但由于暂时还用不到,所以先不写了,等以后用到再补充。
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索函数
, 变量
, 参数
, django
, context
代码
django 模板、django html模板、django 模板语法、django 模板继承、django 模板 if,以便于您获取更多的相关知识。