Django之template操作
一、模板渲染的原理
(一)使用
模板渲染首先有一个模板对象Template,然后有一个上下文对象Context,通过render方法进行渲染,最后返回字符串,render方法的本质还是调用了HttpResponse方法。
>>> python manange.py shell (进入该django项目的环境)
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'shenjianping'})
>>> t.render(c)
'My name is shenjianping.' # 同一模板,多个上下文,一旦有了模板对象,可以通过它渲染多个context
# 当然使用同一模板源渲染多个context,只进行 一次模板创建,然后多次调用render方法渲染会更为高效:
# 每次循环都创建了模板
for name in ('aa', 'bb'):
t = Template('Hello, {{ name }}')
print(t.render(Context({'name': name}))) # 只创建一次模板,更为高效
t = Template('Hello, {{ name }}')
for name in ('aa', 'bb'):
print(t.render(Context({'name': name})))
(二)源码的简单流程
1、Engine
Engine类位于django.template.Engine,这个类主要是将settings中模板的配置读入:
TEMPLATES = [
{
'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',
],
},
},
]
在Engine类的初始化方法中:
class Engine: ...
def __init__(self, dirs=None, app_dirs=False, context_processors=None,
debug=False, loaders=None, string_if_invalid='',
file_charset='utf-8', libraries=None, builtins=None, autoescape=True):
if dirs is None:
dirs = []
if context_processors is None:
context_processors = []
if loaders is None:
loaders = ['django.template.loaders.filesystem.Loader']
if app_dirs:
loaders += ['django.template.loaders.app_directories.Loader']
if not debug:
loaders = [('django.template.loaders.cached.Loader', loaders)]
else:
if app_dirs:
raise ImproperlyConfigured(
"app_dirs must not be set when loaders is defined.")
if libraries is None:
libraries = {}
if builtins is None:
builtins = [] self.dirs = dirs
self.app_dirs = app_dirs
self.loaders = loaders ...
根据传递的app_dirs的布尔值获取加载模板的类loaders:
django.template.loaders.app_directories.Loader
另外,Engine类提供了获取默认的模板引擎的方法:
def get_default():
"""
Return the first DjangoTemplates backend that's configured, or raise
ImproperlyConfigured if none are configured. This is required for preserving historical APIs that rely on a
globally available, implicitly configured engine such as: >>> from django.template import Context, Template
>>> template = Template("Hello {{ name }}!")
>>> context = Context({'name': "world"})
>>> template.render(context)
'Hello world!'
"""
# Since Engine is imported in django.template and since
# DjangoTemplates is a wrapper around this Engine class,
# local imports are required to avoid import loops.
from django.template import engines
from django.template.backends.django import DjangoTemplates
for engine in engines.all():
if isinstance(engine, DjangoTemplates):
return engine.engine
raise ImproperlyConfigured('No DjangoTemplates backend is configured.')
2、Loaders
在settings.py文件的配置中,传入的app_dirs=True,所以使用的loaders是:
django.template.loaders.app_directories.Loader
在这里它做这么几件事:
- 根据默认参数templates获取模板路径并将路径转化成元组形式
- 提供get_template方法,此函数使用给定名称(比如get_template('index.html'))加载模板并返回一个 Template对象(这里的Template对象类型假设就是后台默认配置的DjangoTemplates)
- 提供select_template方法,select_template
()
就像get_template()
,除了它采用模板名称列表,比如select_template([index.html,user.html])。它按顺序尝试每个名称并返回存在的第一个模板。
3、Template
- Template对象是通过上述Loaders类中的get_template方法返回得到的对象:
class DjangoTemplates(BaseEngine): app_dirname = 'templates' ... def get_template(self, template_name):
try:
return Template(self.engine.get_template(template_name), self)
except TemplateDoesNotExist as exc:
reraise(exc, self)
...
- Template类:
class Template: def __init__(self, template, backend):
self.template = template
self.backend = backend @property
def origin(self):
return self.template.origin def render(self, context=None, request=None):
context = make_context(context, request, autoescape=self.backend.engine.autoescape)
try:
return self.template.render(context)
except TemplateDoesNotExist as exc:
reraise(exc, self.backend)
- 使用render渲染
注意这个render是django.shortcuts.render不是Template中的render
def render(request, template_name, context=None, content_type=None, status=None, using=None):
"""
Return a HttpResponse whose content is filled with the result of calling
django.template.loader.render_to_string() with the passed arguments.
"""
content = loader.render_to_string(template_name, context, request, using=using)
return HttpResponse(content, content_type, status)
调用loader中的render_to_string方法,而这个方法返回的就是上述Template类中render方法的返回值,但是最后还是以HttpResponse的方法返回内容
def render_to_string(template_name, context=None, request=None, using=None):
"""
Load a template and render it with a context. Return a string. template_name may be a string or a list of strings.
"""
if isinstance(template_name, (list, tuple)):
template = select_template(template_name, using=using)
else:
template = get_template(template_name, using=using)
return template.render(context, request)
这就是使用render渲染的简单过程。
总结:
- Engine类从配置文件读取模板配置信息
- Loaders类加载模板,默认加载DjangoTemplates
- Template类使用的是其render方法
二、模板语言
(一)变量
变量从上下文输出一个值,这是一个类似于dict的对象,它将键映射到值,在html中进行渲染时使用{{}}来渲染变量。
- 字符串
from django.shortcuts import render,HttpResponse def test(request): return render(request,'index.html',{'first_name': 'John', 'last_name': 'Doe'})
在index.html渲染:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> {{ first_name }}
{{ last_name }} </body>
</html>
- 字典查找,对象属性查找和列表索引查找使用实现
def test(request):
#字典
dict = {'first_name': 'John', 'last_name': 'Doe'}
#列表
list = ['first_name','last_name']
#对象
book_obj = models.Book.objects.get(id=2) return render(request,'index.html',{'dict':dict,'list':list,'book_obj':book_obj})
在index.html渲染:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--字典-->
{{ dict.keys }}{{ dict.values }}
<!--列表-->
{{ list.0 }}
<!--对象-->
{{ book_obj.title }}
</body>
</html>
(二)内置标签
使用大括号与%的组合进行表示{% tag %},标签在渲染过程中提供任意逻辑。例如,标签可以输出内容,用作控制结构(if或for结构),从数据库中获取内容,甚至允许访问其他模板标签。
1、{% if %}
该标签计算一个变量,如果该变量是“真”(即存在,不为空,并且不是假布尔值)的数据块的内容被输出:{% if %}
{% if num >= 150 %} {% if num > 200 %}
<p>num大于200</p>
{% else %}
<p>num大于100小于200</p>
{% endif %} {% elif num < 150 %}
<p>num小于150</p> {% else %}
<p>num等于150</p> {% endif %}
注意:
(1){% if %}语句以{% endif %}结尾;
(2){% if %}允许在同一标记中使用both and
和or
子句, and
优先级高于or,
例如:
{% if user_list and role_list or depart_list %} #这将被解释为:
if (user_list and role_list) or depart_list:
(3){% if %} 标签接受and,or或者not来测试多个变量值或者否定一个给定的变量;
{% if not user_list or role_list %}
There are no usersor there are some roles.
{% endif %} {% if user_list and not role_list %}
There are some usersor and no roles.
{% endif %}
(4){% if %}标签可以使用运算符 ==(等于)
,!=(不等于)
,<(小于)
,>(大于)
, <=(小于等于)
,>=(大于等于)
,in(在...中)
2、{% for %}
(1)遍历列表
循环遍历列表中的每个item,使item在上下文变量中可用。
{% for item in list %} <!--循环列表-->
{{ item }} <!--取出列表中的每一个值-->
{% endfor %}
当然,也可以反向循环列表,加入reversed参数即可:
{% for item in list reversed %} <!--反向循环列表-->
{{ item }} <!--取出列表中的每一个值-->
{% endfor %}
(2)遍历字典
{% for %}标签支持遍历字典:
{% for key, value in dict.items %}
{{ key }}: {{ value }}
{% endfor %}
如果直接遍历,得到的value值:
{% for item in dict %}
{{ item }} <!--value值-->
{% endfor %}
(3)forloop
在遍历过程中,系统不支持中断循环,也不支持continue语句,但是{% for %}标签内置了一个forloop模板变量,这个可以提供一些循环信息:
#for循环设置循环中可用的许多变量: 变量 描述
forloop.counter 循环的当前迭代(1索引)
forloop.counter0 循环的当前迭代(0索引)
forloop.revcounter 循环结束时的迭代次数(1索引)
forloop.revcounter0 循环结束时的迭代次数(0索引)
forloop.first 如果这是第一次通过循环,则为真
forloop.last 如果这是最后一次循环,则为真
forloop.parentloop 对于嵌套循环,这是围绕当前循环的循环
变量的简单使用:
#forloop.counter {% for item in list %}
{{ item }}{{ forloop.counter }}
{% endfor %} #forloop.first 如果这是第一次通过循环,则为真,在某些情况下还是很有用的,比如加入样式 {% for user in user_list%}
{% if forloop.first %}
<li class="first">
{% else %}
<li>
{% endif %}
{{ user}}
</li>
{% endfor %}
注意:forloop变量只能在循环中得到,当模板解析器到达{% endfor %}时forloop就消失了
(4){% for %}标签可以嵌套
#{%for%}之间的嵌套
{% for country in countries %}
{{ country.title}}
<ul>
{% for city in country.city_list %}
<li>{{ city }}</li>
{% endfor %}
</ul>
{% endfor %} #{%for%}与{%if%}之间的嵌套
{% for country in countries %}
{% if country.title =='abx' %}
{{ country}}
{% endif %}
{% endfor %}
3、{% empty %}
该for
标签可以使用一个可选选项,如果给定的列表为空或无法找到,就显示该标签下的内容
{% for i in list %}
<li>{{ forloop.counter }}----{{ i }}</li>
{% empty %}
<li>this is empty!</li>
{% endfor %}
<!--
1----1
2----2
3----3
如果list为空列表,就显示this is empty!
-->
4、{% csrf_token %}
用于生成csrf_token验证码,用于防止跨站攻击验证,这里会生成一个隐藏的input标签,包含键值对一起提交给后台验证。
#使用方式 #在任何使用POST表单的模板中,对于内部url,使用元素csrf_token标记<form>,例如: <form method="post">
{% csrf_token %}
...
</form>
注意:后台如果使用render_to_response()方法,不会生效。
5、{% url %}
返回与给定视图和可选参数匹配的绝对路径引用(不带域名的URL)
{% url 'some-url-name' v1 v2 %} #第一个参数是URL模式名称。它可以是带引号的文字或任何其他上下文变量。其他参数是可选的,应该是以空格分隔的值,这些值将用作URL中的参数。上面的例子显示了传递位置参数。
或者,可以使用关键字语法: {% url 'some-url-name' arg1=v1 arg2=v2 %}
#不要在一次调用中混合使用位置和关键字语法。应该存在URLconf所需的所有参数。
例如:
#路由
path('user/<int:id>/', views.user,name='user-detail'),
#表单
<form action="{% url "user-detail" user.id%}" >
<input type="text">
<input type="submit"value="提交">
{%csrf_token%}
</form>
6、{% with %}
以更简单的名称代替复杂变量。
{% with total=book.authors.count %}
{{ total }}
{% endwith %} #或者这样书写 {% with book.authors.count as total %}
{{ total }}
{% endwith %}
当然也支持多个上下文变量:
{% with x=abc y=jkl %}
{{x}}--{{y}}
{% endwith %}
- {% verbatim %}
停止模板引擎渲染此块标记的内容
#传递变量
def index(request):
k=''
return render(request,'index.html',locals()) #不会渲染m变量
{% verbatim %}
{{ k }}
{% endverbatim %} #页面效果
{{k}}
- {% autoescape%}
#要控制模板的自动转义
{{ value}} #如果value为<a href="#">跳转</a>,输出将是<a href="#">跳转</a> #假如使用autoescape标签 {% autoescape off %}
{{ value }}
{% endautoescape %}
#输出将是“跳转”
更多请查看:https://docs.djangoproject.com/zh-hans/2.0/ref/templates/builtins/#built-in-tag-reference
(三)内置过滤器
过滤器通过修改要显示的变量,从而使变量的值变成所期望的值。
#语法格式:
{{obj|filter:param}}
注意:冒号(:)后只能传一个参数,但是过滤器可以用于控制语句中,如{% if ****%}{% endif %},所以用的频率较高。
1、add
#给变量加上相应的值
{{ value|add:'' }}
此过滤器将首先尝试将两个值强制转换为整数。如果失败,它将尝试将值一起添加到一起。
2、addslashes
# 给变量中的引号前加上斜线
{{ value|addslashes}} #如果value为:"I'm using Django",输出将是"I\'m using Django"
3、capfirst
#首字母大写 如果value是"django",输出将是"Django"。 {{ value|capfirst }}
4、cut
# 从字符串中移除指定的字符 {{ value|cut:" " }} #移除空格
#如果value是"String with spaces",输出将是 "Stringwithspaces"
5、date
#格式化日期字符串 import datetime
value=datetime.datetime.now() {{ value|date:'Y-m-d' }}
6、default
# 如果值是False,就替换成设置的默认值,否则就是用本来的值
{{ value|default:"nothing" }} #如果value是""(空字符串),则输出为nothing。
7、default_if_none
#如果值是None,就替换成设置的默认值,否则就使用本来的值
{{ value|default_if_none:"nothing" }} #如果value是None,输出将是nothing。
8、escape
#转义字符串的HTML。具体来说,它使这些替换: < 转换为 <
> 转换为 >
' (单引号)转换为 '
" (双引号)转换为 "
& 转换为 & #使用
{% autoescape off %}
{{ title|escape }}
{% endautoescape %}
9、first
#返回列表中的第一个项目
{{ value|first }} #如果value是列表['a','b','c','d'],则输出将是'a'
10、last
#返回列表中的最后一个项目
{{ value|first }} #如果value是列表['a','b','c','d'],则输出将是'd'
11、length
#返回列表的长度
{{ value|length}} #如果value是列表['a','b','c','d'],则输出将是4
12、floatformat
#如果在没有参数的情况下使用,则将浮点数舍入到一个小数位 - 但前提是要显示小数部分 {{ value|floatformat }} #如果value为12.21,输出将是12.2
#如果value为12.00,输出将是12
#如果value为12.71,输出将是12.3
13、join
#使用字符串连接列表,如Python str.join(list) {{ value|join:" // " }} #如果value是列表,则输出将是字符串 。['a', 'b', 'c']"a // b // c"
14、lower
#将字符串全部转换为小写 {{ value|lower }} #如果value是ABC,输出将是abc
注意:与之相对的是upper
15、urlencode
#转义值以在URL中使用
{{ value|urlencode }} #如果value是"http://www.yw.com/?page=1&pageSize=3",输出将是
"http%3A//www.yw.com/%3Fpage%3D1%26pageSize%3D3"
更多详情参见:https://docs.djangoproject.com/zh-hans/2.0/ref/templates/builtins/#ref-templates-builtins-filters
(四)自定义过滤器和标签
自定义标签应按照以下步骤执行:
1、在app中创建templatetags目录
注意:在创建templatetags目录不要忘了__init__.py
文件,以确保该目录作为一个Python的包装处理
2、创建任意 .py 文件,如:my_tags.py
from django import template
from django.utils.safestring import mark_safe register = template.Library() #register的名字是固定的,不可改变 #自定制过滤器
@register.filter
def filter_multi(v1,v2):
return v1 * v2 #自定制标签
@register.simple_tag
def simple_tag_multi(v1,v2):
return v1 * v2 @register.simple_tag
def my_input(id,arg):
result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
return mark_safe(result)
3、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py
#注意放在html文件的第一行 {% load my_tags %}
4、使用simple_tag和filter
{% load ‘my_tags’%} <!--首行--> {{ num|filter_multi:3 }} <!--假设num:12,输出则是:36--> {% simple_tag_multi 3 10 %} <!--参数不限,但不能放在if for语句中-->
{% simple_tag_multi num 5 %} <!--假设num:12,输出则是:60-->
注意:过滤器可以用于if等控制语句中,但是标签不行
{% if price|filter_price:10 > 400 %}
{{ price|filter_price:20 }}
{% endif %}
更多请参考:https://docs.djangoproject.com/zh-hans/2.0/howto/custom-template-tags/
三、模板继承
模板继承主要是为了提高代码的重用性,减少开发和维护人员的压力。
(一)父模板
如果发现在多个html文件中有很多的代码是重复,就可以将这些共同的代码提取出来,组成一个公共模板(可以称之为父模板),其它不同的地方只需要预留钩子,比如:
{%block 名称%}
预留区域,可以编写默认内容,也可以没有默认内容
{%endblock 名称%}
注意:字模板继承的名称应与父模板保持一致,并且不能有重名。
base.html:
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>yw</title>
<link rel="stylesheet" href="{% static 'stark/plugins/bootstrap/css/bootstrap.css' %} "/>
<link rel="stylesheet" href="{% static 'stark/plugins/font-awesome/css/font-awesome.css' %} "/>
<style>
body {
margin: 0;
}
</style>
{% block css %}{% endblock %}<!--预留css-->
</head>
<body>
<div class="pg-header">
</div>
<div class="pg-body">
<div class="left-menu">
</div>
<div class="right-body">
{% block content %} {% endblock %}<!--预留内容-->
</div>
</div> <script src="{% static 'stark/js/jquery-3.3.1.min.js' %} "></script>
<script src="{% static 'stark/plugins/bootstrap/js/bootstrap.js' %} "></script>
{% block js %} {% endblock %} <!--预留js-->
</body>
</html>
(二)子模板
在继承父模板时,需要使用标签extends并且将其写在子模板文件的第一行。
{% extends "父模板路径"%}
在继承时,可以不用填充父模板中的预留区域,此时使用的就是默认内容;当然也可以将预留block进行填充,如果在填充后仍然需要默认内容可以进行获取,通过:
{% block content %}
填充实际内容
{{block.super}}<!--用于获取父模板预留block content中的内容-->
{% endblock %}
index.html:
{% extends 'base.html' %}<!--继承父模板-->
{% load staticfiles %}
{% block css %}
<style>
textarea{
min-height: 200px;
}
</style>
{% endblock %}
{% block content %}
<form class="change" method="post" novalidate>
{% csrf_token %}
</form>
</div>
{% endblock %}
{% block js %}
<script src="{% static "stark/js/m2m.js" %}"></script>
<script src="{% static "stark/js/pop.js" %}"></script>
{% endblock %}
(三)总结
1、子模板继承父模板的关键标签{% extends %} 必须位于第一行,否则,模板继承将不起作用。
2、子模板不必定义父模板中所有的block代码块,按需定义即可。
3、如果发现多个html文件中有共同的代码,考虑将其移动到父模板中,并用block预留钩子。
4、不允许在同一个模板中定义多个同名的 {% block %} 。
5、如果需要访问父模板中block的内容,使用 {{ block.super }}这个标签。
Django之template操作的更多相关文章
- Django之Template
模板层(template) 概念: 模板与html的区别: 模板=html+模板语法 模板语法: 1 变量: {{}} 深度查询: 通过句点符. 列表,字典 clas ...
- django模板(template)
模板层(template) 你可能已经注意到我们在例子视图中返回文本的方式有点特别. 也就是说,HTML被直接硬编码在 Python代码之中. 1 2 3 4 def current_datetime ...
- 六、Django之Template
一.Template由来 1.任何前端页面的改动都和后端有关: 2.前端HTML和后端python分开能让网站更加清晰: 3.前后端分离的趋势下,专业的事交给专业的人做. 二.Django中的temp ...
- Django之Model操作
Django之Model操作 本节内容 字段 字段参数 元信息 多表关系及参数 ORM操作 1. 字段 字段列表 AutoField(Field) - int自增列,必须填入参数 primary_ke ...
- Python之路【第二十二篇】:Django之Model操作
Django之Model操作 一.字段 AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bi ...
- Django之ORM操作
Django之ORM操作 前言 Django框架功能齐全自带数据库操作功能,本文主要介绍Django的ORM框架 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计 ...
- Django之ORM操作(聚合 分组、F Q)
Django之ORM操作(聚合 分组.F Q) 聚合 aggregate()是QuerySet的一个终止子句,也就是说,他返回一个包含一些键值对的字典,在它的后面不可以再进行点(.)操作. 键的名 ...
- Python学习---Django的基础操作180116
Django创建数据库操作 django流程之model实例 settigs.py:更改Django2.0.1的配置,更新为之前的路径配置 'DIRS': [os.path.join(BASE_DIR ...
- Django 2.0 学习(16):Django ORM 数据库操作(下)
Django ORM数据库操作(下) 一.增加表记录 对于表单有两种方式: # 方式一:实例化对象就是一条表记录france_obj = models.Student(name="海地&qu ...
随机推荐
- 利用Kubernetes(K8S)部署JAVA项目
一.jar包和war包区别 首先简单介绍一下jar包和war包区别,当时就没分清,导致部署总是傻傻分不清楚. jar包:jar包就是java的类进行编译生成的class文件就行打包的压缩包.里面是一些 ...
- lsyncd+rsync文件实时同步
1.rsync两端都需要安装 yum -y install rsync 2.提供lsyncd的安装源 rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x ...
- C# WinForm 提示框延迟自动关闭
有时候我们需要弹出个提示框然后让它自己关闭,然而实际使用中的弹出框确实阻塞进程,网上貌似有一种另类的解决方式,大致思路是把弹出框放到另外的一个窗体上,直接贴代码 主窗体 using System; u ...
- 【串线篇】概述SpringMvc和spring整合
SpringMVC和Spring整合的目的:分工明确: SpringMVC的配置文件就来配置和网站转发逻辑以及网站功能有关的(视图解析器,文件上传解析器,支持ajax,xxx):springmvc.x ...
- AI行业精选日报_人工智能(12·24)
特斯拉完全自动驾驶进步:新系统能识别交通信号标志 12 月 24 日,有美国车友称,升级最新的特斯拉 2019.40.50 系统后已经可以识别红绿灯和停车标志(目前仅限搭载 HW3.0 硬件的车型), ...
- 【leetcode】991. Broken Calculator
题目如下: On a broken calculator that has a number showing on its display, we can perform two operations ...
- Linux文件归档工具——cpio
一cpio的介绍 功能:复制文件从或到归档 cpio命令是通过重定向的方式将文件进行打包备份,还原恢复的工具,它可以解压以“.cpio”或者“.tar”结尾的文件. cpio [选项] > 文件 ...
- testNG之顺序执行
@Test testNG1.java: import org.testng.annotations.Test; public class testNG1 { @Test public void t ...
- 耗时十个月的德国APS,教会我的学习方法
考过了准备了10个月的Aps ,想送给关注我的8175个粉丝,一份礼物,感谢你们看的起我,对我的支持和关注. 这份礼物,我就大言不惭的称之为:达令的学习方法. 我的考试经历:高考两次,中戏艺考三试,导 ...
- LiveTelecast直播平台技术图谱skill-map
#直播平台技术图谱 ----##直播 ----###采集- **iOS** * HTTP Live Streaming * DirectShow- **Android** * setPreviewCa ...