<学习笔记>从零开始自学Python-之-web应用框架Django(三)Django模板

        上一篇文章的示例中返回文本的方式有些特别,即在 Python 代码中硬编码 HTML,如下所 示:

def now(request):
    now=datetime.datetime.now()
    show="你好,现在的时间是{}".format(now)
    return HttpResponse(show)

这样做虽然便于说明视图的工作方式,但是直接在视图中硬编码 HTML 不是个好主意。

原因如下:

• 只要想修改页面的设计就要修改 Python 代码。网站的设计肯定比底层的 Python 代码变化频繁,因此 如果无需修改 Python 代码就能改变设计,那多方便。

• 这只是十分简单的示例。网页模板往往包含几百行 HTML 和脚本。在这样的混乱中排错和诊断程序代码简直是噩梦。

• 编写 Python 代码和设计 HTML 是两件不同的事,多数专业的 Web 设计团队会把这两件事交给不同的 人做(甚至不同的部门)。设计师和 HTML/CSS 程序员完成工作不应该需要编辑 Python 代码。

• 如果编写 Python 代码的程序员和设计模板的设计师能同时工作,而不用等到一个人编辑好包含 Python 和 HTML 的文件之后再交给下一个人,工作效率能得到提升。

鉴于此,把页面的设计与 Python 代码分开,结果更简洁,也更易于维护。为此,我们可以使用 Django 的模板系统。

1、模板基础知识

Django 模板是一些文本字符串,作用是把文档的表现与数据区分开。模板定义一些占位符和基本的逻辑(模 板标签),规定如何显示文档。通常,模板用于生成 HTML,不过 Django 模板可以生成任何基于文本的格式。

下面看一个简单的模板示例 sample.html

<html>
<head>
<title>武侠三班</title>
</head>
<body>

<h1>通知</h1>
<p>本次中期比武,{
   
   { student1_name }}获得了第一名</p>
<p>获得优秀的同学还有{
   
   {student2_name}}、{
   
   {student3_name}}、{
   
   {student4_name}}等</p>
<p>希望其他同学以他们为榜样,苦练武功,争取下次比武取得好成绩</p>

<ul>
{% for k in student_list %}

{% if k.0 == 'male' %}
<li>{
   
   { k.1 }}大侠</li>
{% else %}
<li>{
   
   { k.1 }}女侠</li>
{% endif %}


{% endfor %}
</ul>



<p>落款:你们尊敬的老师<br />{
   
   { teacher }}</p>
<p>日期: {
   
   { now|date:"D d M Y" }} </p>
</body>
</html>

这个模板的大部分内容是 HTML,内含一些变量和模板标签。

下面详细说明。

• 两对花括号包围的文本(如 { { student1_name }})是变量,意思是“把指定变量的值插入这里”。

• 一对花括号和百分号包围的文本(如 {% if ordered_warranty %})是模板标签。

标签的定义相当宽泛:只要能让模板系统“做些事”的就是标签。

• 这个示例模板中有一个 for 标签{% for s in student_list %}和一个 if 标签({% if k.0 == 'male' %})。

for 标签的作用与 Python 中的 for 语句很像,用于迭代序列中的各个元素。

if 标签的作用是编写逻辑判断语句。 注意,{% else %} 是可选的。

• 最后的落款部分包含一个过滤器,这是调整变量格式最为便利的方式。对这个示例中的 { { now|date:"D d M Y" }}来说,我们把 now 变量传给 date 过滤器,并且为 date 过滤器指定 "D d M Y" 参数。date 过滤器使用参数指定的格式格式化日期。过滤器使用管道符号(|)依附,类似 于 Unix 管道。

Django 模板能访问多个内置的标签和过滤器,后面我们会展开讨论。

2、模板的使用

首先需要向Django说明模板文件的路径,修改settings.py,修改 TEMPLATES 中的 DIRS 为 [os.path.join(BASE_DIR, 'templates')],如下所示:

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',
            ],
        },
    },
]

然后我们在views.py里面修改视图函数,这次我们不用HttpResponse了,我们引入render函数

def classnotice(request):
    now=datetime.datetime.now()
    students={1:'郭靖',2:'杨过',3:'王语嫣',4:'赵敏'}
    students2=[['male','郭靖'],['male','杨过'],['female','王语嫣'],['female','赵敏']]
    context={}
    context['student1_name']=students[1]
    context['student2_name']=students[2]
    context['student3_name']=students[3]
    context['student4_name']=students[4]
    context['student_list']=students2
    context['teacher']='王重阳'
    context['now']=now

    
    return render(request,'sample.html',context=context,status=200)

我们重新写一个视图函数classnotice,定义上面模板文件里要用到的各个变量,然后新建一个空字典 context,把各个变量都装进这个字典去。这里要注意,context字典的键值就是模板要引用的变量名,如模板里面要用到变量 student1_name,那这里就要用context['student1_name]=students[1]来赋值,这样这个值才能传给模板,让模板以这个变量名去调用它。数字、字符串、甚至列表、对象等都可以作为字典的值传送给模板。

注意:如果是字典、列表、对象等作为值传给模板,模板在调用的时候,跟Python里面有所不同,Python里面用中括号 students[1]来取值,模板里面统统用(变量+"."+属性、方法 等等)的形式来取值,如上面的变量表示为 students.1的形式来取值

最后一行返回值,这里用到了render函数,第一个参数是request请求对象,第二个参数是模板名称,第三个参数就是这个context字典,所有的变量值通过这个字典传送,第四个参数是响应值,200表示成功响应。

同样的,我们要去配置urls.py,增加一个路径'notice',映射的函数是上面我们写的 classnotice

from django.contrib import admin
from django.urls import path
from newweb.views import *

urlpatterns = [
    path('',now),
    path('admin/', admin.site.urls),
    path('hello/',hello),
    path('student/<int:id>/',student),
    path('student/<str:sex>/',student2),
    path('notice',classnotice),
]

最后我们还要注意查看一下sample.html文件的位置,根据setting.py的路径,它应该在内层newweb文件夹同级别即manage.py所在的文件夹下新建一个 templates文件夹,然后sample.html文件放在这里,后面所有的模板文件都应该放在这里

一切OK,我们访问地址 http://127.0.0.1:8000/notice,看到了下面的内容

3、模板的继承

       我们上面举的例子都是很简单的网页,而真实的网站是很复杂的,大家往往发现,网站有很多网页,但是这些网页有差不多同样的页头、边栏和页脚等等,这些同样的内容如果每次都要重复去写会增加很多工作量,也不利于维护,所以使用Django模板的继承功能是很优雅的解决方案。

模板继承是指创建一 个基底“骨架”模板,即父模板,包含网站的所有通用部分,并且定义一些“块”,让子模板覆盖。

父模板用于放置可重复利用的内容,子模板继承父模板的内容,并放置自己的内容。

父模板

标签 block...endblock: 父模板中的预留区域,该区域留给子模板填充差异性的内容,不同预留区域名字不能相同。

{% block 名称 %} 
预留给子模板的区域,可以设置设置默认内容
{% endblock 名称 %}

子模板

子模板使用标签 extends 继承父模板

{% extends "父模板路径"%} 

子模板如果没有设置父模板预留区域的内容,则使用在父模板设置的默认内容,当然也可以都不设置,就为空。

子模板设置父模板预留区域的内容:

{ % block 名称 % }
内容 
{% endblock 名称 %}

下面我们先创建一个名称为base.html的父模板

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>武侠世界</title>
<style>
* {
  box-sizing: border-box;
}
 
body {
  font-family: Arial;
  padding: 10px;
  background: #f1f1f1;
}
 
/* 头部标题 */
.header {
  padding: 30px;
  text-align: center;
  background: white;
}
 
.header h1 {
  font-size: 50px;
}
 
/* 导航条 */
.topnav {
  overflow: hidden;
  background-color: #333;
}
 
/* 导航条链接 */
.topnav a {
  float: left;
  display: block;
  color: #f2f2f2;
  text-align: center;
  padding: 14px 16px;
  text-decoration: none;
}
 
/* 链接颜色修改 */
.topnav a:hover {
  background-color: #ddd;
  color: black;
}
 
/* 创建两列 */
/* Left column */
.leftcolumn {   
  float: left;
  width: 75%;
}
 
/* 右侧栏 */
.rightcolumn {
  float: left;
  width: 25%;
  background-color: #f1f1f1;
  padding-left: 20px;
}
 
/* 图像部分 */
.fakeimg {
  background-color: #aaa;
  width: 100%;
  padding: 20px;
}
 
/* 文章卡片效果 */
.card {
  background-color: white;
  padding: 20px;
  margin-top: 20px;
}
 
/* 列后面清除浮动 */
.row:after {
  content: "";
  display: table;
  clear: both;
}
 
/* 底部 */
.footer {
  padding: 20px;
  text-align: center;
  background: #ddd;
  margin-top: 20px;
}
 
/* 响应式布局 - 屏幕尺寸小于 800px 时,两列布局改为上下布局 */
@media screen and (max-width: 800px) {
  .leftcolumn, .rightcolumn {   
    width: 100%;
    padding: 0;
  }
}
 
/* 响应式布局 -屏幕尺寸小于 400px 时,导航等布局改为上下布局 */
@media screen and (max-width: 400px) {
  .topnav a {
    float: none;
    width: 100%;
  }
}
</style>
</head>
<body>

<div class="header">
  <h1>武侠世界</h1>
  <p>武林人士自己的线上江湖</p>
  {% block title %}
  武侠二班主页
  {% endblock title %}
</div>

<div class="topnav">
  <a href="#">武侠一班</a>
  <a href="#">武侠二班</a>
  <a href="#">武侠三班</a>
  <a href="#" style="float:right">华山论剑</a>
</div>


<div class="leftcolumn">
    {% block main %}
    
    <p>
        这是base模板
    </p>

{% endblock main %}

</div>





  <div class="rightcolumn">
    <div class="card">
      <h2>武力排名</h2>
      <ul>
        {% for k in student_list %}
        
        {% if k.0 == 'male' %}
        <li>{
   
   { k.1 }}大侠</li>
        {% else %}
        <li>{
   
   { k.1 }}女侠</li>
        {% endif %}
        
        
        {% endfor %}
        </ul>  
    </div>
    <div class="card">
      <h3>武林资讯</h3>
      <div class="fakeimg"><p>杨过找到了失散多年的姑姑,久别重逢让人唏嘘不已</p></div>
      <div class="fakeimg"><p>西毒欧阳锋重现江湖,西域多人已经遇害</p></div>
      <div class="fakeimg"><p>武侠二班老同学聚会,黄药师回母校参观</p></div>
    </div>
    <div class="card">
      <h3>关注下一届武林大会选举</h3>
      <p>传闻乔峰和张无忌公将要争夺武林盟主之位</p>
    </div>
  </div>
</div>

<div class="footer">
  <h2>底部区域</h2>
  <span class="link">
    <a href="javascript:void(0)">Django</a> |
    <a href="javascript:void(0)">网站搭建</a> |
    <a href="javascript:void(0)">人生苦短</a> |
    <a href="javascript:void(0)">我用Python</a>
</span>
<p class="bottom-content">
    <span>地址: 上海市武林大道武侠大厦2805室  </span>
    <span>联系方式: 021-88888888</span>
</p>
<p class="copyright-desc">
    Copyright &copy; 2005 - 2021 武侠世界. All Rights Reserved
</p>
</div>

</body>
</html>

这里我为了方便,把css的设定写在了head里面,正式开发的话css应该单独存一个文件。

我们直接双击打开看看效果

 可以看到,这个base.html搭建了网站的整体框架,包括页头、页尾、边栏等,还有一些固定信息,比如网站标题、尾部的联系地址、公司资料等。

其中标题那里 留下了给子模版继承的部分

{% block title %}

  武侠二班主页

  {% endblock title %}

这个中间“武侠二班的主页”是可以被子模板替换的部分,等下我们把它换成三班

中间左边的部分,也留下了可以继承的部分

{% block main %}

       <p>

        这是base模板

    </p>

{% endblock main %}

这里面就是要放我们子模板的主体部分了,所以我把它命名为“main”,这个名称是自己取的,但是记得这里取什么名字,到子模板中调用的时候要用到这个名字

现在我们去写子模板,把我们之前的sample.html网页的内容作为子模板内容放进去

{% extends "base.html" %}
{% block title %}武侠三班的小窝{% endblock %}

{% block main %}
<h1>通知</h1>
<p>本次中期比武,{
   
   { student1_name }}获得了第一名</p>
<p>获得优秀的同学还有{
   
   {student2_name}}、{
   
   {student3_name}}、{
   
   {student4_name}}等</p>
<p>希望其他同学以他们为榜样,苦练武功,争取下次比武取得好成绩</p>

<ul>
{% for k in student_list %}

{% if k.0 == 'male' %}
<li>{
   
   { k.1 }}大侠</li>
{% else %}
<li>{
   
   { k.1 }}女侠</li>
{% endif %}


{% endfor %}
</ul>



<p>落款:你们尊敬的老师<br />{
   
   { teacher }}</p>
<p>日期: {
   
   { now|date:"D d M Y" }} </p>

{% endblock %}

注意:

开头要用{% extends "base.html" %}表示引用base.html作为父模板

{% block title %}武侠三班的小窝{% endblock %}就是把父模板中title那一部分的内容换成我们自己的

{% block main %}  和 {% endblock main %} 之间的内容替换掉父模板的内容

保存为class3.html ,把views.py中的classnotice函数改一下,render的第二个参数改为class3.html

意思classnotice这个视图函数映射的模板改为了class3.html,就是我们刚刚创建的继承的页面

def classnotice(request):
    now=datetime.datetime.now()
    students={1:'郭靖',2:'杨过',3:'王语嫣',4:'赵敏'}
    students2=[['male','郭靖'],['male','杨过'],['female','王语嫣'],['female','赵敏']]
    context={}
    context['student1_name']=students[1]
    context['student2_name']=students[2]
    context['student3_name']=students[3]
    context['student4_name']=students[4]
    context['student_list']=students2
    context['teacher']='王重阳'
    context['now']=now

    
    return render(request,'class3.html',context=context,status=200)

然后我们访问http://127.0.0.1:8000/notice (因为我们之前的URL没有改,所以这里还是访问/notice,调用classnotice这个视图函数),看到效果如下:

 可以看到页头、页尾和右边的边栏都和父模板一样的,只是中间的内容替换了,用这种方法,可以快速搭建网站,因为很多网页只是部分内容不一样,而且这样让整个网站看上去风格统一。

关于模板继承,有一些注意事项:

• 如果模板中有 {% extends %},必须是模板中的第一个标签。否则,模板继承不起作用。

• 一般来说,基模板中的 {% block %} 标签越多越好。记住,子模板无需定义父模板中的全部块,因此 可以为一些块定义合理的默认内容,只在子模板中覆盖需要的块。钩子多总是好的。

• 如果发现要在多个模板中重复编写相同的代码,或许说明应该把那些代码移到父模板中的一个 {% block %} 标签里。 • 如果需要从父模板中的块里获取内容,使用 { { block.super }},这是一个“魔法”变量,提供父模板中渲染后的文本。向块中添加内容,而不是完全覆盖时就可以这么做。

• 在同一个模板中不能为多个 {% block %} 标签定义相同的名称。之所以有这个限制,是因为 block 标签是双向的。即,block 标签不仅标识供填充的空位,还用于定义填充父模板中空位的内容。如果一 个模板中有两个同名的块,那么父模板就不知道使用哪个块里的内容。

• 传给 {% extends %} 的模板名称使用与 get_template() 相同的方法加载。即,模板在 DIRS 设置定义的 目录中,或者在当前 Django 应用的“templates”目录里。

• 多数情况下,{% extends %} 的参数是字符串,不过如果直到运行时才知道父模板的名称,也可以用变量。通过这一点可以做些动态判断。

猜你喜欢

转载自blog.csdn.net/qq_41597915/article/details/126092557