3Python全栈之路系列之D

简介:

Python全栈之路系列之Django模板


模板是一个文本,用于分离文档的表现形式和内容,模板定义了占位符以及各种用于规范文档该如何显示的各部分基本逻辑(模板标签)。模板通常用于产生HTML,但是Django的模板也能产生任何基于文本格式的文档。


如何使用模板系统

在Python代码中使用Django模板的最基本方式如下:

  1. 可以用原始的模板代码字符串创建一个Template对象,Django同样支持用指定模板文件路径的方式来创建Template对象;

  2. 调用模板对象的render方法,并且传入一套变量context。它将返回一个基于模板的展现字符串,模板中的变量和标签会被context值替换。

模板渲染

模板渲染

一旦你创建一个Template对象,你可以用context来传递数据给它。一个context是一系列变量和它们值的集合。

context在Django里表现为Context类,在django.template模块里,她的构造函数带有一个可选的参数: 一个字典映射变量和它们的值。调用Template对象的render()方法并传递context来填充模板:

1
2
3
4
5
6
7
8
9
10
# manage.py shell命令会在启动解释器之前告诉Django使用哪个配置文件
E:\mysite>python manage.py shell
Python  3.5 . 2  (v3. 5.2 : 4def2a2901a5 , Jun  25  2016 22 : 18 : 55 ) [MSC v. 1900  64  bit (AMD64)] on win32
Type  "help" "copyright" "credits"  or  "license"  for  more information.
(InteractiveConsole)
>>>  from  django.template  import  Context, Template
>>> t  =  Template( 'My name is {{ name }}.' )
>>> c  =  Context({ 'name' 'Stephane' })
>>> t.render(c)
'My name is Stephane.'

这就是使用Django模板系统的基本规则:写模板,创建Template对象,创建Context,调用render()方法。

深度变量的查找

我们通过context传递的简单参数值主要是字符串,还有一个datetime.date范例。然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。

在Django模板中遍历复杂数据结构的关键是句点字符(.)

最好是用几个例子来说明一下。 比如,假设你要向模板传递一个Python字典,要通过字典键访问该字典的值,可使用一个句点:

1
2
3
4
5
6
>>>  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.'

点语法也可以用来引用对象的方法。 例如,每个Python字符串都有upper()isdigit()方法,你在模板中可以使用同样的句点语法来调用它们:

1
2
3
4
5
6
>>>  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'

句点也可用于访问列表索引,例如:

1
2
3
4
5
>>>  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.'

当模板系统在变量名中遇到点时,按照以下顺序尝试进行查找:

  1. 字典类型查找(比如foo["bar"])

  2. 属性查找(比如foo.bar)

  3. 方法调用(比如foo.bar())

  4. 列表类型索引查找(比如foo[bar])

模板标签

if/else

{ % if % }标签检查一个变量的值是否为真或者等于另外一个值,如果为真,系统会执行{ % if % }{ %endif % }之间的代码块,例如:

1
2
3
{ %  if  today_is_weekend  % }
     <p>Welcome to the weekend!< / p>
{ %  endif  % }

{ % else % }标签是可选的:

如果不为真则执行{ % else % }{ % endif % }之间的代码块

{ % if % }标签不允许在同一个标签中同时使用andor,因为逻辑上可能模糊的,比如这样的代码是不合法的:

1
{ %  if  athlete_list  and  coach_list  or  cheerleader_list  % }

系统不支持用圆括号来组合比较操作,如果你确实需要用到圆括号来组合表达你的逻辑式,考虑将它移到模板之外处理,然后以模板变量的形式传入结果吧,或者,仅仅用嵌套的{ % if % }标签替换吧,就像这样:

1
2
3
4
5
{ %  if  athlete_list  % }
     { %  if  coach_list  or  cheerleader_list  % }
         We have athletes,  and  either coaches  or  cheerleaders!
     { %  endif  % }
{ %  endif  % }

多次使用同一个逻辑操作符是没有问题的,但是我们不能把不同的操作符组合起来。 例如,这是合法的:

{% if athlete_list or coach_list or parent_list or teacher_list %}

并没有{ % elif % }标签,使用嵌套的{ % if % }标签来达成同样的效果:

1
2
3
4
5
6
7
8
{ %  if  athlete_list  % }
     <p>Here are the athletes: {{ athlete_list }}.< / p>
{ %  else  % }
     <p>No athletes are available.< / p>
     { %  if  coach_list  % }
         <p>Here are the coaches: {{ coach_list }}.< / p>
     { %  endif  % }
{ %  endif  % }

一定要用{ % endif % }关闭每一个{ % if % }标签

  • for

{ % for % }允许我们在一个序列上迭代,与Python的 for 语句的情形类似,循环语法是for X in YY是要迭代的序列而X是在每一个特定的循环中使用的变量名称。每一次循环中,模板系统会渲染在{ % for % } 和{ % endfor % }之间的所有内容。

例如创建一个运动员列表athlete_list变量,给标签增加一个reversed使得该列表被反向迭代,显示这个列表

1
2
3
4
5
<ul>
     { %  for  athlete  in  athlete_list  reversed   % }
         <li>{{ athlete.name }}< / li>
     { %  endfor  % }
< / ul>

在执行循环之前先检测列表的大小为空时输出一些特别的提示:

1
2
3
4
5
{ %  for  athlete  in  athlete_list  % }
     <p>{{ athlete.name }}< / p>
{ %  else  % }或者{ %  empty  % }
     <p>There are no athletes. Only computer programmers.< / p>
{ %  endfor  % }

Django不支持退出循环操作,也不支持continue语句。

在每个{ % for % }循环里有一个称为forloop的模板变量。这个变量有一些提示循环进度信息的属性。

属性 描述
forloop.counter 表示当前循环的执行次数的整数计数器,从1开始计数
forloop.counter0 从0开始计数
forloop.revcounter 循环中剩余项的整型变量,开始执行时值为列表中的总数,结束时为1
forloop.revcounter0 以0做为结束索引
forloop.first 是一个布尔值,如果该迭代是第一次执行,那么它被置为False
forloop.last 是一个布尔值;在最后一次执行循环时被置为True
forloop.parentloop 指向当前循环的上一级循环的 forloop 对象的引用(在嵌套循环的情况下)
forloop 仅仅能够在循环中使用,在模板解析器碰到{ % endfor % }标签后,forloop就不可访问了

ifequal/ifnotequal

{ % ifequal % }标签比较两个值,当他们相等时,显示在{ % ifequal % }{ % endifequal % }之中所有的值。

下面的例子比较两个模板变量usercurrentuser:

{% ifequal user currentuser %}    <h1>Welcome!</h1>{% endifequal %}

{ % if % }类似,{ % ifequal % }支持可选的{ % else % } 标签,其他任何类型,例如Python的字典类型、列表类型、布尔类型,不能用在{ % ifequal % }中。

注释

就像HTML或者Python,Django模板语言同样提供代码注释,注释使用{# #}

{# This is a comment #}

注释的内容不会在模板渲染时输出。

过滤器

模板过滤器是在变量被显示前修改它的值的一个简单方法,过滤管道可以被套接,既是说,一个过滤器管道的输出又可以作为下一个管道的输入,如此下去,下面的例子实现查找列表的第一个元素并将其转化为大写:

{{ my_list|first|upper }}

有些过滤器有参数。 过滤器的参数跟随冒号之后并且总是以双引号包含。 例如:

{{ bio|truncatewords:"30" }}

这个将显示变量 bio 的前30个词。

方法 描述
addslashes 添加反斜杠到任何反斜杠、单引号或者双引号前面
date 按指定的格式字符串参数格式化date或者datetime对象
length 返回变量的长度

date范例:

{{ pub_date|date:"F j, Y" }}

在视图中使用模板

加载模板

为了减少模板加载调用过程及模板本身的冗余代码,Django提供了一种使用方便且功能强大的API,用于从磁盘中加载模板,要使用此模板加载API,首先你必须将模板的保存位置告诉框架。

打开settings.py配置文件,找到TEMPLATES这项设置,设置一个目录,用于模板加载机制在哪里查找模板

1
2
# 首先你要导入os模块
'DIRS' : [os.path.join(BASE_DIR,  'templates' )],

然后再templates目录下面创建一个名为current_datetime.html的文件,内容为:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang = "en" >
<head>
     <meta charset = "UTF-8" >
     <title>Title< / title>
< / head>
<body>
It  is  now {{ current_date }}.
< / body>
< / html>

urls.py文件内容

1
2
3
4
5
6
7
from  django.conf.urls  import  url
from  django.contrib  import  admin
from  DarkerProjects.views  import  current_datetime
urlpatterns  =  [
     url(r '^admin/' , admin.site.urls),
     url(r '^time/' , current_datetime)
]

views.py文件内容

1
2
3
4
5
6
7
8
9
10
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()
     # 该get_template()函数以模板名称为参数,在文件系统中找出模块的位置,打开文件并返回一个编译好的Template对象
     =  get_template( 'current_datetime.html' )
     html  =  t.render(Context({ 'current_date' : now}))
     return  HttpResponse(html)

get_template()方法会自动为你连接已经设置的TEMPLATES配置的DIRS目录和你传入该法的模板名称参数。比如,你的DIRS目录设置为'/home/django/mysite/templates',上面的get_template()调用就会为你找到/home/django/mysite/templates/current_datetime.html这样一个位置。

如果get_template()找不到给定名称的模板,将会引发一个TemplateDoesNotExist异常。

然后我们就可以通过python manage.py runserver来运行Django了,通过访问http://127.0.0.1:8000/time/会返回当前时间的字符串。

render_to_response()

Django提供了一个捷径,让你一次性地载入某个模板文件,渲染它,然后将此作为HttpResponse返回。

该捷径就是位于django.shortcuts模块中名为render_to_response()的函数。

下面就是使用render_to_response()重新编写过的current_datetime范例:

1
2
3
4
5
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})

在上面的代码中我们不再需要导入get_templateTemplateContextHttpResponse。相反,我们导入django.shortcuts.render_to_responseimport datetime继续保留.

current_datetime函数中,我们仍然进行now计算,但模板加载、上下文创建、模板解析和HttpResponse创建工作均在对render_to_response()的调用中完成了。由于render_to_response()返回HttpResponse对象,因此我们仅需在视图中return该值。

render_to_response()的第一个参数必须是要使用的模板名称,如果要给定第二个参数,那么该参数必须是为该模板创建Context时所使用的字典。如果不提供第二个参数,render_to_response()使用一个空字典.

locals()技巧

思考一下我们对 current_datetime 的最后一次赋值:

1
2
3
def  current_datetime(request):
     now  =  datetime.datetime.now()
     return  render_to_response( 'current_datetime.html' , { 'current_date' : now})

很多时候,就像在这个范例中那样,你发现自己一直在计算某个变量,保存结果到变量中(比如前面代码中的now),然后将这些变量发送给模板。尤其喜欢偷懒的程序员应该注意到了,不断地为临时变量和临时模板命名有那么一点点多余。不仅多余,而且需要额外的输入。

如果你是个喜欢偷懒的程序员并想让代码看起来更加简明,可以利用 Python 的内建函数locals()。它返回的字典对所有局部变量的名称与值进行映射。因此,前面的视图可以重写成下面这个样子:

1
2
3
def  current_datetime(request):
     current_date  =  datetime.datetime.now()
     return  render_to_response( 'current_datetime.html' locals ())

在此,我们没有像之前那样手工指定context字典,而是传入了locals()的值,它囊括了函数执行到该时间点时所定义的一切变量。因此,我们将now变量重命名为current_date,因为那才是模板所预期的变量名称。在本例中,locals()并没有带来多大的改进,但是如果有多个模板变量要界定而你又想偷懒,这种技术可以减少一些键盘输入。

使用locals()时要注意是它将包括所有的局部变量,它们可能比你想让模板访问的要多。在前例中,locals()还包含了request。对此如何取舍取决你的应用程序。

get_template()中使用子目录

把模板存放于模板目录的子目录中是件很轻松的事情,只需在调用get_template()时,把子目录名和一条斜杠添加到模板名称之前,如:

1
=  get_template( 'dateapp/current_datetime.html' )

由于render_to_response()只是对get_template()的简单封装,你可以对render_to_response()的第一个参数做相同处理。

1
return  render_to_response( 'dateapp/current_datetime.html' , { 'current_date' : now})

对子目录树的深度没有限制,你想要多少层都可以,只要你喜欢,用多少层的子目录都无所谓。

include模板标签

内建模板标签{ % include % },该标签允许在(模板中)包含其它的模板的内容,标签的参数是所要包含的模板名称,可以是一个变量,也可以是用单/双引号硬编码的字符串,每当在多个模板中出现相同的代码时,就应该考虑是否要使用{ % include % }来减少重复。

下面这两个例子都包含了nav.html模板

1
2
{ %  include  'nav.html'  % }
{ %  include  "nav.html"  % }

下面的例子包含了includes/nav.html模板的内容:

{% include 'includes/nav.html' %}

下面的例子包含了以变量template_nam的值为名称的模板内容:

1
{ %  include template_name  % }

如果{ % include % }标签指定的模板没找到,Django将会在下面两个处理方法中选择一个:

  1. 如果DEBUG设置为True,你将会在Django错误信息页面看到TemplateDoesNotExist异常;

  2. 如果DEBUG设置为False,该标签不会引发错误信息,在标签位置不显示任何东西;

模板继承

本质上来说,模板继承就是先构建一个基础模板框架,而后在其子模板中对它所包含站点公用部分和定义块进行重载。

定义基础模板,该框架之后将由子模板所继承:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang = "en" >
<head>
     <meta charset = "UTF-8" >
     <title>{ %  block title  % }{ %  endblock  % }< / title>
< / head>
<body>
     <h1>My helpful timestamp site< / h1>
     { %  block content  % }{ %  endblock  % }
     { %  block footer  % }
     <hr>
     <p>Thanks  for  visiting my site.< / p>
     { %  endblock  % }
< / body>
< / html>

这个叫做base.html的模板定义了一个简单的HTML框架文档,我们将在本站点的所有页面中使用,子模板的作用就是重载、添加或保留那些块的内容。

每个{ % block % }标签所要做的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。

修改current_datetime.html文件内容为

1
2
3
4
5
6
7
{ %  extends  "includes/base.html"  % }
 
{ %  block title  % }The current time{ %  endblock  % }
 
{ %  block content  % }
<p>It  is  now {{ current_date }}.< / p>
{ %  endblock  % }

以下是其工作方式:

在加载current_datetime.html模板时,模板引擎发现了{ % extends % }标签,注意到该模板是一个子模板,模板引擎立即装载其父模板,即本例中的base.html

此时,模板引擎注意到base.html中的三个{ % block % }标签,并用子模板的内容替换这些block因此,引擎将会使用我们在{ % block title % }中定义的标题,对{ % block content % }也是如此,所以,网页标题一块将由{ % block title % }替换,同样地,网页的内容一块将由{ % block content % }替换。

注意由于子模板并没有定义footer块,模板系统将使用在父模板中定义的值。父模板{ % block % }标签中的内容总是被当作一条退路。

继承并不会影响到模板的上下文,换句话说,任何处在继承树上的模板都可以访问到你传到模板中的每一个模板变量。

你可以根据需要使用任意多的继承次数,使用继承的一种常见方式是下面的三层法

  1. 创建base.html模板,在其中定义站点的主要外观感受,这些都是不常修改甚至从不修改的部分。

  2. 为网站的每个区域创建base_SECTION.html模板(例如,base_photos.htmlbase_forum.html)。这些模板对base.html进行拓展,并包含区域特定的风格与设计;

  3. 为每种类型的页面创建独立的模板,例如论坛页面或者图片库,这些模板拓展相应的区域模板。

这个方法可最大限度地重用代码,并使得向公共区域(如区域级的导航)添加内容成为一件轻松的工作,以下是使用模板继承的一些诀窍

  1. 如果在模板中使用{ % extends % } ,必须保证其为模板中的第一个模板标记,否则,模板继承将不起作用;

  2. 一般来说,基础模板中的{ % block % }标签越多越好,记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好;

  3. 如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个{ % block % }

  4. 如果你需要访问父模板中的块的内容,使用` block`.`super `这个标签吧,这一个魔法变量将会表现出父模板中的内容,如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了;

  5. 不允许在同一个模板中定义多个同名的{ % block % } ,存在这样的限制是因为block标签的工作方式是双向的,也就是说,block标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个相同名称的{ % block % }标签,父模板将无从得知要使用哪个块的内容;

  6. { % extends % }对所传入模板名称使用的加载方法和get_template()相同。 也就是说,会将模板名称被添加到TEMPLATE_DIRS设置之后;

  7. 多数情况下,{ % extends % }的参数应该是字符串,但是如果直到运行时方能确定父模板名,这个参数也可以是个变量,这使得你能够实现一些很酷的动态功能;










本文转自 Edenwy  51CTO博客,原文链接:http://blog.51cto.com/edeny/1925026,如需转载请自行联系原作者
目录
相关文章
|
5月前
|
Linux 数据库 数据安全/隐私保护
Python web Django快速入门手册全栈版,共2590字,短小精悍
本教程涵盖Django从安装到数据库模型创建的全流程。第一章介绍Windows、Linux及macOS下虚拟环境搭建与Django安装验证;第二章讲解项目创建、迁移与运行;第三章演示应用APP创建及项目汉化;第四章说明超级用户创建与后台登录;第五章深入数据库模型设计,包括类与表的对应关系及模型创建步骤。内容精炼实用,适合快速入门Django全栈开发。
242 1
|
6月前
|
数据采集 自然语言处理 Java
Playwright 多语言一体化——Python/Java/.NET 全栈采集实战
本文以反面教材形式,剖析了在使用 Playwright 爬取懂车帝车友圈问答数据时常见的配置错误(如未设置代理、Cookie 和 User-Agent),并提供了 Python、Java 和 .NET 三种语言的修复代码示例。通过错误示例 → 问题剖析 → 修复过程 → 总结教训的完整流程,帮助读者掌握如何正确配置爬虫代理及其它必要参数,避免 IP 封禁和反爬检测,实现高效数据采集与分析。
404 3
Playwright 多语言一体化——Python/Java/.NET 全栈采集实战
|
设计模式 前端开发 数据库
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第27天】本文介绍了Django框架在Python Web开发中的应用,涵盖了Django与Flask等框架的比较、项目结构、模型、视图、模板和URL配置等内容,并展示了实际代码示例,帮助读者快速掌握Django全栈开发的核心技术。
677 45
|
安全 数据库 开发者
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第26天】本文详细介绍了如何在Django框架下进行全栈开发,包括环境安装与配置、创建项目和应用、定义模型类、运行数据库迁移、创建视图和URL映射、编写模板以及启动开发服务器等步骤,并通过示例代码展示了具体实现过程。
364 2
|
JSON 数据库 开发者
FastAPI入门指南:Python开发者必看——从零基础到精通,掌握FastAPI的全栈式Web开发流程,解锁高效编码的秘密!
【8月更文挑战第31天】在当今的Web开发领域,FastAPI迅速成为开发者的热门选择。本指南带领Python开发者快速入门FastAPI,涵盖环境搭建、基础代码、路径参数、请求体处理、数据库操作及异常处理等内容,帮助你轻松掌握这一高效Web框架。通过实践操作,你将学会构建高性能的Web应用,并为后续复杂项目打下坚实基础。
719 0
|
前端开发 JavaScript 测试技术
Python中的全栈开发
【6月更文挑战第6天】本文探讨了Python在全栈开发中的应用,展示了如何利用Python的Django和Flask框架进行后端开发,以及与JavaScript前端框架的集成。文中通过示例介绍了Django和Flask的基本用法,并讨论了全栈开发中的前端集成、CORS问题、数据传输、身份验证、异步编程、性能优化、日志记录、错误处理、测试、安全性、数据库集成、实时通信、缓存和扩展功能。此外,还强调了全栈开发涉及的团队协作、项目管理和用户体验,指出Python为全栈开发提供了强有力的支持。
|
测试技术 数据库 开发者
Python全栈测试开发Chapter11 Mock测试
总结起来,Mock测试是一种有效的隔离测试环境、提高测试效率的方法,它让我们能够在不依赖外部条件的情况下进行全面的单元测试。在Python全栈测试中,Mock的应用是一种非常实用的技能。
174 0
|
Python
老男孩&路飞学城Python全栈
老男孩&路飞学城的Python全栈开发重点班由ALEX老师主讲,核心教学内容,100多天课程,近100G资料,含基础到实战。一线技术专家亲授,以案例教学引导学生逐步进入项目实战。
333 1
老男孩&路飞学城Python全栈
|
安全 前端开发 JavaScript
Python 全栈安全(三)(4)
Python 全栈安全(三)
103 1
|
XML SQL 安全
Python 全栈安全(三)(3)
Python 全栈安全(三)
201 1