模板
模板用于快速生成动态页面返回给客户端,模板是⼀个文本,用于分离文档的表现
形式和内容。 模板定义了占位符以及各种用于规范文档该如何显示的模板标签。
模板通常用于产生HTML,但是Django的模板也能产生任何基于文本格式的文档。
模板包含两部分:
- html代码
- 模板标签
⼀、模板位置
-
在应用中建立templates目录,好处不需要注册,不好的地方,有多个应用的
时候不能复用页面 -
第⼆种是放在工程的目录下,好处是如果有多个应用,可以调用相同的页面,
需要注册- 需要修改项目的配置文件settings.py
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 这里默认使用Django自带的模板引擎 'DIRS': [os.path.join(BASE_DIR, 'templates')] # 模板绝对路径,如果是自己手动创建的django项目,则该内容为空,需要手动设置 , 'APP_DIRS': True, # 是否在应用目录下查找模板文件 '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', ], }, }, ]
Django 模板查找机制: Django首先会查找DIRS下配置的模板路径,如果不在的话,则是在每个 app 的 templates
文件夹中找(而不只是当前 app 中的代码只在当前的 app 的 templates 文件
夹中找)。各个 app 的 templates 形成⼀个文件夹列表,Django 遍历这个列
表,⼀个个文件夹进行查找,当在某⼀个文件夹找到的时候就停止,所有的都
遍历完了还找不到指定的模板的时候就是 Template Not Found (过程类似于
Python找包)。这样设计有利当然也有弊,有利是的地方是⼀个app可以用另
⼀个app的模板文件,弊是有可能会找错了。所以我们使用的时候在
templates 中建立⼀个 app 同名的文件夹,这样就好了。
⼆、模板的渲染
2.1 loader加载
好处是可以加载⼀次模板,然后进行多次渲染
from django.template import loader #导入loader
def index(request):
temp = loader.get_template('index.html')
# 渲染模板,生出html源码
res = temp.render(context={'content':'hello index'})
print(res)
return HttpResponse(res)
2.2 render
from django.shortcuts import render
render(request,templatesname,context=None)
参数:
request:请求对象
templatesname:模板名称
context:参数字典,必须是字典
三、模板语法
django模板中包括两部分:变量和内置标签。变量会在模板渲染时被其值代替,
内置标签负责逻辑控制。
3.1 变量
变量在模板中的表示为:{{ 变量名 }},变量名就是render中context中的键。变量
可以基本类型中的数值、字符串、布尔,也可以是字典、对象、列表等。django
提供了点号来访问复杂数据结构。
- 列表、元组的元素可以使用索引引用,不能使用负索引,语法:变量.索引
- 字典: 字典变量.key
- 对象: 对象.属性 对象.方法名(方法不能有参数)
当模板系统在变量名中遇到点时,按照以下顺序尝试进行查找:
- 字典类型查找
- 属性查找
- 方法调用
- 列表类型索引
如果模板中引用变量未传值,则会被置为空,不会报错,除非你对其进行了操作。
3.2 过滤器
过滤器是在变量显示之前修改它的值的⼀个方法,过滤器使用管道符。过滤器可以
串联调用
语法格式:
{{ 变量|方法 }}
常见的过滤器方法:
方法名 | 作用 | 示例 |
---|---|---|
default | 缺省值 | {{ li|default:"缺省值"}} |
default_if_none | 如果变量是none,则显示缺省值 | {{ value|default_if_none:'"hello" }} |
cut | 从字符中删除指定字符 | {{ value|cut:" "}} 删除value中所有的空格 |
length | 获取字符串或列表的长度 | {{ str1|length}} |
lower | 将所有字母都变为⼩写 | {{ value|lower}} |
upper | 将所有字母都变为大写 | {{ value|upper}} |
truncatechars | 截取字符串前n个字符 | {{ value|truncatechars:9 }} |
date | 格式化日期字符串 | {{ value|date:"Y-m-d H:i:s"}} |
add | 增加变量的值 | {{ num|add:"3"}} |
divisibleby | 把变量的值除以指定值 | {{ value|divisibleby:"3"}} |
first | 获取列表第⼀个元素 | {{ value|first }} |
last | 获取列表最后⼀个元素 | {{ value|last }} |
join | 将列表内容链接为一个字符串 | {{ value|join:'-' }} |
autoescape | 设置或取消转义 | {% autoescape off %}{{ data }}{%endautoescape %} |
-
自定义过滤器
内置过滤器功能有限,如果不能满足需求,可以自己定义过滤器。
-
在app里创建⼀个包:templatetags(名字必须是这个,且为包)
-
在包里创建⼀个py文件
from django import template # 实例化自定义过滤器注册对象 register = template.Library() # name代表在模板中使用的过滤器的名称 @register.filter(name='hello') def hello(value,arg): """ :param value: 传给hello过滤的值 :param arg: hello自带的参数 :return: """ return value + str(arg) @register.filter('time_ago') def time_ago(value): """ 定义⼀个距离当前时间多久之前的过滤器 :param value: :return: 1.如果时间间隔⼩于1分钟内,那么就显示刚刚 2.如果时间间隔大于1分钟⼩于1⼩时,那么就显示xx分钟前 3.如果时间间隔大于1⼩时⼩于24⼩时,那么就显示xx⼩时前 4.如果时间间隔大于24⼩时⼩于30天,那么就显示xx天前 5.如果时间间隔大于30天,那么就显示具体时间 """ if not isinstance(value, datetime.datetime): return value now = datetime.datetime.now() timestamp = (now - value).total_seconds() if timestamp < 60: return '刚刚' elif timestamp >= 60 and timestamp < 60 * 60: return '{}分钟前'.format(int(timestamp / 60)) elif timestamp >= 60 * 60 and timestamp < 60 * 60 * 24: return '{}⼩时前'.format(timestamp / 60 / 60) elif timestamp >= 60 * 60 * 24 and timestamp < 60 * 60 * 23 * 30: return‘{}天前'.format(int(timestamp/68/68/24)) else: return value.strftime('%Y-%m-%d %H:%M')
-
在模板中使用
{% load customfilter %} #加载自定义过滤器的模块 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ name |hello:' how are you' }} #使用自定义过滤器 </body> </html>
-
3.3 内置标签
语法:{% tag %}
1.if标签
{% if express1 %}
# to do
{% elif express2 %}
# to do
{% else %}
# to do
{% endif %}
- if表达式中使用以下运算符(优先级从⾼到低):
- < >= <= == !=
- in 、not in
- is、is not
- not
- and
- or
- 不要在表达式中使用(),可以使用if嵌套实现功能
- 不⽀持 if 3 < b < 5这种写法
2.for
遍历可迭代对象
{% for x in y %}
...
{% endfor %}
- 反向迭代(reversed)
{% for value in c [1,2,3,4,5] reversed %}
<span>{{ value }}---</span>
{% endfor %}
- empty 当可迭代对象为空或不存在时执行,否则不执行
{% for value in c %}
<span>{{ value }}---</span>
{% empty %}
数据不存在
{% endfor %}
- 字典迭代
# e = {'a1':20,'b1':40}
{% for k,v in e.items %}
<div>{{ k }}---{{ v }}</div>
{% endfor %}
- 获取for循环迭代的状态
变量名称 | 变量说明 |
---|---|
forloop.counter | 获取迭代的索引 从1开始 |
forloop.counter0 | 获取迭代的索引 从0开始 |
forloop.revcounter | 迭代的索引从最大递减到1 |
forloop.revcounter0 | 迭代的索引从最大递减到0 |
forloop.first | 是否为第⼀次迭代 |
forloop.last | 是否为最后⼀次迭代 |
forloop.parentloop | 获取上层的迭代对象 |
{% for i in c %}
<li>{{ forloop.first }}</li>
<li>{{ forloop.last }}</li>
<li>{{ forloop.counter }}</li>
<li>{{ forloop.counter0 }}</li>
<li>{{ forloop.revcounter }}</li>
<li>{{ forloop.revcounter0 }}</li>
{% endfor %}
3. ifequal/ifnotequal
用于判断两个值相等或不等的
{% ifequal var var %}
{% endifequal %}
{% ifnotequal var var %}
{% endifnotequal %}
4.注释
- 单行注释
{# 注释内容 #}
- 多行注释
{% comment %}
...
{% endcomment %}
5.跨站请求伪造 csrf
防止网站受第三方服务器的恶意攻击(确定表单到底是不是本网站的表单传递过来
的)。csrf相当于在表达中增加了⼀个隐藏的input框,用于向服务器提交⼀个唯⼀
的随机字符串用于服务器验证表单是否是本服务器的表单。
使用:
settings.py
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]
表单里
<form action=
"" method="post">
{% csrf_token %}
<input type="text" name="username">
<p><input type="submit"></p>
</form>
- 全站禁用csrf
#在settings中设置
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
- 局部禁用csrf
#在不想检验csrf的视图函数前添加装饰器@csrf_exempt。
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
def csrf1(request):
pass
- ajax验证csrf
Ajax提交数据时候,携带CSRF:
a. 放置在data中携带
<form method="POST" action="/csrf1.html">
{% csrf_token %}
<input id="username" type="text" name="username" />
<input type="submit" value="提交"/>
<a onclick="submitForm();">Ajax提交</a>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
function submitForm(){
var csrf = $('input[name="csrfmiddlewaretoken"]').val();
var user = $('#user').val();
$.ajax({
url: '/csrf1.html',
type: 'POST',
data: { "user":user,'csrfmiddlewaretoken': csrf},
success:function(arg){
console.log(arg);
}
})
}
</script>千
注意:
csrf的意义在于 给每⼀个表单都设置⼀个唯⼀的csrf的值 并且cookie也存储⼀份
当提交表单过来的时候 判断cookie中的值 和csrf_token中的值 是否都为本网站生
成的 如果验证通过则提交 否则 403
6.模板导入标签( include)
可以把指定html文件代码导入到当前文件,实现模板代码的复用/重用。语法格
式:
{% include '路径/xxx.html' %}
7.url标签
在模板中url标签可用于反向解析
<h2><a href="{% url 'App:index' %}">动态生成路由地址不带参的跳转</a>
</h2>
<h2><a href="{% url 'App:args1' 1 2 %}">动态生成路由地址带参的跳转</a>
</h2>
<h2><a href="{% url 'App:args1' num1=1 num2=2 %}">动态生成路由地址带关
键字参数的跳转</a></h2>
四、模板继承
在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代
码?Django 解决此类问题的首选方法是使用⼀种优雅的策略—— 模板继承 。
本质上来说,模板继承就是先构造⼀个基础框架模板,而后在其子模板中对它所包
含站点公用部分和定义块进行重载。
- {% extends %} 继承父模板
- {% block %} 子模板可以重载这部分内容。
- {{ block.super }}调用父模板的代码
使用继承的⼀种常见方式是下面的三层法:
- 创建base.html模板,在其中定义站点的主要外观感受。这些都是不常修改甚
⾄从不修改的部分。 - 为每种类型的页面创建独立的模板,例如论坛页面或者图片库。这些模板拓展
相应的区域模板。 - 自己的页面继承自模板,覆盖父模板中指定block
注意事项:
- 如果在模板中使用 {% extends %} ,必须保证其为模板中的第⼀个模板标记。
否则,模板继承将不起作 用。 - ⼀般来说,基础模板中的 {% block %} 标签越多越好。
- 如果发觉自己在多个模板之间有重复代码,你应该考虑将该代码放置到父模板
的某个 {% block %} 中。 - 不在同⼀个模板中定义多个同名的 {% block %} 。
- 多数情况下, {% extends %} 的参数应该是字符,但是如果直到运行时方能确
定父模板名称,这个参数也 可以是个变量。
五、静态资源配置
什么是静态资源:css、js、images 需要从外部导入的资源
5.1创建static文件夹(通常放在根目录下)
5.2需要在settings注册
STATIC_URL='/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static')
]
5.3在模板中使用静态资源
{% load static %} #放置到模板开头
<img src="/static/img/img.jpeg" alt=""> #硬编码
<img src="{% static 'img/img.jpeg' %}" alt=""> #动态写法,建议用这种
六、jinja2模板引擎配置
- 安装jinja2模板引擎
pip install jinja2
- 设置jinja2环境变量
# -*- coding: utf-8 -*-
from jinja2 import Environment
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
def jinja2_environment(**options):
env = Environment(**options)
env.globals.update({
'static': staticfiles_storage.url,
'url': reverse,
})
return env
-
配置(setting.py)
- 独立使用jinja2,不使用Django模板引擎
#独立使用jinja2 INSTALLED_APPS = [ #'django.contrib.admin', # 注释了admin ..... ] #模板配置 TEMPLATES = [{ 'BACKEND': 'django.template.backends.jinja2.Jinja2',#jinja2模版 'DIRS': [ os.path.join(BASE_DIR, 'templates'),#模版文件位置 ], 'APP_DIRS': True, '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', ], 'environment': 'App.jinja2_env.environment', # 配置环 境,jinja的配置文件位置 }, }, ]
- 两个同时使用
#模板配置 TEMPLATES = [{ 'BACKEND': 'django.template.backends.jinja2.Jinja2',#jinja2模 版 'DIRS': [ os.path.join(BASE_DIR, 'templates2'),#修改模版文件位置 ], 'APP_DIRS': True, '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', ], 'environment': 'App.jinja2_env.environment', # 配置环 境,jinja的配置文件位置 }, }, { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, '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', ], }, }, ]
-
静态资源和url
<p>
<a href="{{ url('app:index') }}">dddd</a>
</p>
<img src="{{ static('images/1.jpg') }}" alt="">