Loading

day 63 模板层 (T)

1 模板简介

​ 在刚刚介绍完的视图层中我们提到,浏览器发送的请求信息会转发给视图函数进行处理,而视图函数在经过一系列处理后必须要有返回信息给浏览器。如果我们要返回html标签、css等数据给浏览器进行渲染,我们可以在视图函数中这么做。

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

​ 上例所示,我们直接将HTML代码放到视图函数里,然后进行返回,这可以使我们很直观地看清楚浏览器从发送请求到看到前端界面内容的这个过程中视图函数的基本工作原理,但是这种将前端代码与后端代码耦合到了一起开发方式,会存在以下问题。

1、程序的可维护性与可扩展性问题
	前端界面一旦需要重新设计、修改,则必须对后端的Python代码进行相应的修改。 然而前端界面的修改往往比后端 
    Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更前端界面的设计,那将会方便得
    多。

2、开发效率问题
	Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将它们分配给不同的人员(甚至不
    同部门)来完成。 专门的程序员去编写 Python代码、专门的设计人员去制作模板,这两项工作同时进行,效率才是最
    高的。

​ 基于上述原因,将前端页面和Python的代码分离是一种不错的开发模式。 为此 Django专门提供了模板系统 (Template System,即模板层)来实现这种模式,这就是本章要具体讨论的问题。

django的模板=HTML代码+模板语法

​ 存放于templates目录下的html文件称之为模板文件,如果我们想要返回的html页面中的数据是动态的,那么必须在html页面中嵌入变量,这便用到了django的模板语法,具体来说,django的模板语法有以下重点

一、变量:{{ 变量名 }}
	1.1 深度查询:句点符的应用
	1.2 过滤器
二、标签:{% 标签名 %}
三、自定义标签和过滤器
四、模板的导入和继承

2 模板语法之变量

2.1 变量的基本使用

​ 如果html代码中的数据不是固定死的,而是动态变化的,则必须在html中嵌入变量,为此,模板语法提供了变量的概念,允许我们在html代码中嵌入变量,我们只需要在视图函数中用render方法为html文件中指定的变量赋值即可,具体用法如下

{{}}:变量相关

{%%}:逻辑相关

示例一:

test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p>{{ msg }}</p>
<p>{{ dic }}</p>
<p>{{ obj }}</p>
<p>{{ li }}</p>
</body>
</html>

我们需要在视图函数中为模板test.html的变量名msg、li、dic、obj、obj_li赋值,views.py内容如下

from django.shortcuts import render

def test(request):
    # 传给模板的变量值可以是任意python类型,如下
    msg='hello world'
    dic={'k1':1,'k2':2}
    class Person(object):
        def __init__(self,name,age):
            self.name=name
            self.age=age

    obj=Person('egon',18)
    li = [1,'aaa',obj]

    return render(request,'test.html',{'msg':msg,'dic':dic,'obj':obj,'li':li})
    # 注意:
    # 1、render函数的第三个参数包含了要传给模板的变量值,是一个字典类型,该字典中的key必须与模板文件中的变量名相对应,render函数会去templates目录下找到模板文件,然后根据字典中的key对应到模板文件中的变量名进行赋值操作,最后将赋值后的模板文件内容返回给浏览器
    # 2、可以将render函数的第三个参数简写为locals(),如下
    return render(request,'test.html',locals()) #locals()会将函数test内定义的名字与值转换为字典中的k与v

示例二:

示例2
视图函数中:
def index(request):
    # 模版语法可以传递的后端python数据类型
    n = 123
    f = 11.11
    s = '我也想奔现'
    b = True
    l = ['小红','姗姗','花花','茹茹']
    t = (111,222,333,444)
    d = {'username':'jason','age':18,'info':'这个人有点意思'}
    se = {'晶晶','洋洋','嘤嘤'}

    def func():
        print('我被执行了')
        return '你的另一半在等你'

    class MyClass(object):
        def get_self(self):
            return 'self'

        @staticmethod
        def get_func():
            return 'func'

        @classmethod
        def get_class(cls):
            return 'cls'
        
        # 对象被展示到html页面上 就类似于执行了打印操作也会触发__str__方法
        def __str__(self):
            return '到底会不会?'  
        
    obj = MyClass()

    return render(request,'index.html',{})  	# 传值方法1:一个个传
    return render(request,'index.html',locals())

"--------------------------------------------------------------------------------------"
html页面中
<p>{{ n }}</p>
<p>{{ f }}</p>
<p>{{ s }}</p>
<p>{{ b }}</p>
<p>{{ l }}</p>
<p>{{ d }}</p>
<p>{{ t }}</p>
<p>{{ se }}</p>
<p>传递函数名会自动加括号调用 但是模版语法不支持给函数传额外的参数:{{ func }}</p>
<p>传类名的时候也会自动加括号调用(实例化){{ MyClass }}</p>
<p>内部能够自动判断出当前的变量名是否可以加括号调用 如果可以就会自动执行  针对的是函数名和类名</p>
<p>{{ obj }}</p>
<p>{{ obj.get_self }}</p>
<p>{{ obj.get_func }}</p>
<p>{{ obj.get_class }}</p>


# django模版语法的取值 是固定的格式 只能采用“句点符” .
<p>{{ d.username }}</p>
<p>{{ l.0 }}</p>
<p>{{ d.hobby.3.info }}</p>
# 即可以点键也可以点索引 还可以两者混用

2.2 深度查询之句点符的使用

​ 当视图函数传给模板的值中包含多个元素时,若想取出其中的单个元素,就必须使用句点符了。

​ 句点符既可以引用容器类型的元素,也可以引用对象的方法,如下

test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!--调用字符串对象的upper方法,注意不要加括号-->
<p>{{ msg.upper }}</p>

<!--取字典中k1对应的值-->
<p>{{ dic.k1 }}</p>

<!--取对象的name属性-->
<p>{{ obj.name }}</p>

<!--取列表的第2个元素,然后变成大写-->
<p>{{ li.1.upper }}</p>

<!--取列表的第3个元素,并取该元素的age属性-->
<p>{{ li.2.age }}</p>


</body>
</html>

2.3 过滤器

​ 在Django的模板语言中,通过使用 过滤器 来改变变量的显示,过滤器类似于python的内置函数,用来把视图传入的变量值加以修饰后再显示,具体语法如下。

{{ 变量名|过滤器名:传给过滤器的参数 }}

 {{ value|filter_name:参数 }}

​ 使用管道符"|"来应用过滤器。

​ 例如:{{ name|lower }}会将name变量应用lower过滤器之后再显示它的值。lower在这里的作用是将文本全都变成小写。

注意事项:

  1. 过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。
  2. 过滤器可以接受参数,例如:{{ sss|truncatewords:30 }},这将显示sss的前30个词。过滤器只能最多有两个参数。
  3. 过滤器参数包含空格的话,必须用引号包裹起来。比如使用逗号和空格去连接一个列表中的元素,如:{{ list|join:', ' }}
  4. '|'左右没有空格没有空格没有空格

Django的模板语言中提供了大约六十个内置过滤器。

常用内置过滤器
1、default
作用:如果一个变量值是False或者为空,使用default后指定的默认值,否则,使用变量本身的值,如果value=’‘则输出“nothing”
{{ value|default:"nothing" }}

2、length
作用:返回值的长度。它对字符串、列表、字典等容器类型都起作用,如果value是 ['a', 'b', 'c', 'd'],那么输出是4
{{ value|length }}

3、filesizeformat
作用:将值的格式化为一个"人类可读的"文件尺寸(如13KB、4.1 MB、102bytes等等),如果 value 是 12312312321,输出将会是 11.5 GB
{{ value|filesizeformat }}

4、date
作用:将日期按照指定的格式输出,如果value=datetime.datetime.now(),按照格式Y-m-d则输出2019-02-02
{{ value|date:"Y-m-d" }}  

5、slice
作用:对输出的字符串进行切片操作,顾头不顾尾,如果value=“egon“,则输出"eg"
{{ value|slice:"0:2" }} 

6、truncatechars
作用:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾,如果value=”hello world egon 嘎嘎“,则输出"hello...",注意8个字符也包含末尾的3个点
{{ value|truncatechars:8 }}

7、truncatewords
作用:同truncatechars,但truncatewords是按照单词截断,注意末尾的3个点不算作单词,如果value=”hello world 
egon 嘎嘎“,则输出"hello world ..."
{{ value|truncatewords:2 }}

8、safe
作用:出于安全考虑,Django的模板会对HTML标签、JS等语法标签进行自动转义,例如value="<script>alert(123)
</script>",模板变量{{ value }}会被渲染成&lt;script&gt;alert(123)&lt;/script&gt;交给浏览器后会被解析成普通字
符”<script>alert(123)</script>“,失去了js代码的语法意义,但如果我们就想让模板变量{{ value }}被渲染的结果又语
法意义,那么就用到了过滤器safe,比如value='<a href="https://www.baidu.com">点我啊</a>',在被safe过滤器处
理后就成为了真正的超链接,不加safe过滤器则会当做普通字符显示’<a href="https://www.baidu.com">点我啊
</a>‘
{{ value|safe }}
<h1>过滤器</h1>
<p>统计长度:{{ s|length }}</p>
<p>默认值(第一个参数布尔值是True就展示第一个参数的值否在展示冒号后面的值):{{ b|default:'啥也不是' }}</p>
<p>文件大小:{{ file_size|filesizeformat }}</p>
<p>日期格式化:{{ current_time|date:'Y-m-d H:i:s' }}</p>
<p>切片操作(支持步长):{{ l|slice:'0:4:2' }}</p>
<p>切取字符(包含三个点):{{ info|truncatechars:9 }}</p>
<p>切取单词(不包含三个点 按照空格切):{{ egl|truncatewords:9 }}</p>
<p>切取单词(不包含三个点 按照空格切):{{ info|truncatewords:9 }}</p>
<p>移除特定的字符:{{ msg|cut:' ' }}</p>
<p>拼接操作:{{ l|join:'$' }}</p>
<p>拼接操作(加法):{{ n|add:10 }}</p>
<p>拼接操作(加法):{{ s|add:msg }}</p>
<p>转义:{{ hhh|safe }}</p>
<p>转义:{{ sss|safe }}</p>
<p>转义:{{ res }}</p>

# 转义
# 前端
	|safe
# 后端
	from django.utils.safestring import mark_safe
	res = mark_safe('<h1>新新</h1>')
"""
以后你在全栈项目的时候 前端代码不一定非要在前端页面书写
也可以现在先在后端写好 然后传递给前端页面
"""

3 模板语法之标签

​ 标签是为了在模板中完成一些特殊功能,语法为{% 标签名 %},一些标签还需要搭配结束标签 {% endtag %}

​ 标签就是Django自带的简单函数

3.1 基本使用

基本使用
"for循环"
	{% for foo in l %}
    <p>{{ forloop }}</p>
    <p>{{ foo }}</p>  一个个元素
	{% endfor %}
  {'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 6, 'revcounter0': 5, 'first': True, 'last': False}

"if判断"
{% if b %}
    <p>baby</p>
{% elif s%}
    <p>都来把</p>
{% else %}
    <p>老baby</p>
{% endif %}


" for与if混合使用"
{% for foo in lll %}
    {% if forloop.first %}
        <p>这是我的第一次</p>
    {% elif forloop.last %}
        <p>这是最后一次啊</p>
    {% else %}
        <p>{{ foo }}</p>
    {% endif %}
    {% empty %}
        <p>for循环的可迭代对象内部没有元素 根本没法循环</p>
{% endfor %}


"处理字典其他方法"
{% for foo in d.keys %}
    <p>{{ foo }}</p>
{% endfor %}
{% for foo in d.values %}
    <p>{{ foo }}</p>
{% endfor %}
{% for foo in d.items %}
    <p>{{ foo }}</p>
{% endfor %}


" with起别名"
{% with d.hobby.3.info as nb  %}
    <p>{{ nb }}</p>
    在with语法内就可以通过as后面的别名快速的使用到前面非常复杂获取数据的方式
    <p>{{ d.hobby.3.info }}</p>
{% endwith %}

3.2 常用标签之for标签

基本使用
#1、遍历每一个元素:
{% for person in person_list %}
    <p>{{ person.name }}</p>
{% endfor %}

#2、可以利用{% for obj in list reversed %}反向循环。

#3、遍历一个字典:
{% for key,val in dic.items %}
    <p>{{ key }}:{{ val }}</p>
{% endfor %}

#4、循环序号可以通过{{ forloop }}显示 
forloop.counter            当前循环的索引值(从1开始)
forloop.counter0           当前循环的索引值(从0开始)
forloop.revcounter         当前循环的倒序索引值(从1开始)
forloop.revcounter0        当前循环的倒序索引值(从0开始)
forloop.first              当前循环是第一次循环则返回True,否则返回False
forloop.last               当前循环是最后一次循环则返回True,否则返回False
forloop.parentloop         本层循环的外层循环

#5、for标签可以带有一个可选的{% empty %} 从句,在变量person_list为空或者没有被找到时,则执行empty子句
{% for person in person_list %}
    <p>{{ person.name }}</p>

{% empty %}
    <p>sorry,no person here</p>
{% endfor %}

案例

url.py
from django.urls import re_path
from app01 import views

urlpatterns = [
    re_path(r'^test/',views.test)
]
view.py
def test(request):
    names=['egon','kevin']
    dic={'name':'egon','age':18,'sex':'male'}

    list1=[]

    return render(request,'test.html',locals())
test.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<hr>
{% for name in names %}
    <p>{{ forloop.counter0 }} {{ name }}</p>
{% endfor %}
<!--
输出结果为:
0 egon
1 kevin
-->

<hr>
{% for name in names reversed %}
    <p>{{ forloop.revcounter0 }} {{ name }}</p>
{% endfor %}
<!--
输出结果为:
1 kevin
0 egon
-->

<hr>
{% for k,v in dic.items %}
    <p>{{ forloop.counter }} {{ k }} {{ v }}</p>
{% endfor %}
<!--
输出结果为:
1 name egon
2 age 18
3 sex male
-->

<hr>
{% for item in list1 %}
    <p>{{ item }}</p>
    {% empty %}
        <p>sorry,no value here</p>
{% endfor %}
<!--
输出结果为:
sorry,no value here
-->

</body>
</html>

3.3 常用标签之if标签

# 1、注意:
{% if 条件 %}条件为真时if的子句才会生效,条件也可以是一个变量,if会对变量进行求值,在变量值为空、或者视图没有为其传值的情况下均为False

# 2、具体语法
{% if num > 100 or num < 0 %}
    <p>无效</p>
{% elif num > 80 and num < 100 %}
    <p>优秀</p>
{% else %}
    <p>凑活吧</p>
{% endif %}

#3、if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。

案例

urls.py
from django.urls import path,register_converter,re_path
from app01 import views

urlpatterns = [
    # 输入http://127.0.0.1:8008/或者http://127.0.0.1:8008/index/都会转发给视图函数index
    re_path(r'^$',views.index),
    re_path(r'^index/$',views.index),
    
    re_path(r'^login/',views.login),

]
views.py
from django.shortcuts import render

def index(request):
    return render(request,'index.html')

def login(request):
    if request.method == 'GET':
        return render(request,'login.html')

    name=request.POST.get('name')
    pwd=request.POST.get('pwd')
    if name == 'egon' and pwd == '123':
        current_user=name
        return render(request,'index.html',locals())
    else:
        msg='账号或密码错误'
        return render(request,'login.html',locals())

在templates目录下新建模板文件index.html与login.html

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

<h3>首页</h3>
<!--
如果用户已经登录,则current_user变量有值,if判断结果为真,会打印变量current_user的值,为当前登录的用户名
如果用户没有登录,则current_user变量无值,if判断结果为假,会打印a标签要求用户先登录
-->
{% if current_user %}
    <p>当前登录用户为:{{ current_user }}</p>
{% else %}
    <p><a href="/login/">请先登录</a></p>
{% endif %}

</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>

<form action="" method="POST">
    {% csrf_token %}
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="password" name="pwd"></p>
    <p><input type="submit" value="提交"></p>
</form>
<!--输错账号密码时的提示信息-->
<p style="color: red">{{ msg }}</p>
</body>
</html>

测试

python manage.py runserver 8008 #在浏览器输入http://127.0.0.1:8008/,然后点击登录,输入账号密码进行验证......

3.4 常用标签之with标签

# with标签用来为一个复杂的变量名起别名,如果变量的值来自于数据库,在起别名后只需要使用别名即可,无需每次都向数据库发送请求来重新获取变量的值
{% with li.1.upper as v %}
    {{ v }}
{% endwith %}

3.5 常用标签之csrf_token标签

# 当用form表单提交POST请求时必须加上标签{% csrf_token%},该标签用于防止跨站伪造请求
<form action="" method="POST">
    {% csrf_token %}
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="password" name="pwd"></p>
    <p><input type="submit" value="提交"></p>
</form>
# 具体工作原理为:
# 1、在GET请求到form表单时,标签{% csrf_token%}会被渲染成一个隐藏的input标签,该标签包含了由服务端生成的一串随机字符串,如<input type="hidden" name="csrfmiddlewaretoken" value="dmje28mFo...OvnZ5">
# 2、在使用form表单提交POST请求时,会提交上述随机字符串,服务端在接收到该POST请求时会对比该随机字符串,对比成功则处理该POST请求,否则拒绝,以此来确定客户端的身份

4 自定义过滤器和标签

当内置的过滤器或标签无法满足我们需求时,我们可以自定义,具体操作步骤如下

先三步走
	1.在应用下创建一个名字”必须“叫templatetags文件夹
	2.在该文件夹内创建“任意”名称的py文件 eg:mytag.py
	3.在该py文件内"必须"先书写下面两句话(单词一个都不能错)
		from django import template
		
		register = template.Library()
"自定义过滤器"
@register.filter(name='baby')
def my_sum(v1, v2):
    return v1 + v2

"使用
{% load mytag %}
<p>{{ n|baby:666 }}</p>

--------------------------------------------------------------------
"自定义标签(参数可以有多个)	类似于自定义函数"
@register.simple_tag(name='plus')
def index(a,b,c,d):
    return '%s-%s-%s-%s'%(a,b,c,d)

"使用"
标签多个参数彼此之间空格隔开
<p>{% plus 'jason' 123 123 123 %}</p>

--------------------------------------------------------------------
"自定义inclusion_tag"
"""
内部原理
	先定义一个方法 
	在页面上调用该方法 并且可以传值
	该方法会生成一些数据然后传递给一个html页面
	之后将渲染好的结果放到调用的位置
"""
@register.inclusion_tag('left_menu.html')
def left(n):
    data = ['第{}项'.format(i) for i in range(n)]
    
    "第一种"
    return {'data':data}  # 将data传递给left_menu.html
    "第二种"
    return locals()  # 将data传递给left_menu.html

"使用"
{% left 5 %}
"总结:当html页面某一个地方的页面需要传参数才能够动态的渲染出来,并且在多个页面上都需要使用到该局部 那么就考虑将该局部页面做成inclusion_tag形式"

示例

1、在settings中的INSTALLED_APPS添加当前app的名字,不然django无法找到自定义的过滤器或标签

settings.py

# 在settings.py中找到该列表,然后加以配置
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'app01', # 添加当前app的名字
]

2、在文件夹app01中创建子文件夹templatetags(文件夹名只能是templatetags)

3、在templatetags新建任意.py文件,如my_tags.py,在该文件中自定义过滤器或标签,文件内容如下

from django import template
register = template.Library() # 注意变量名必须为register,不可改变

#1、自定义过滤器
@register.filter
def my_multi_filter(v1 ,v2): # 自定义的过滤器只能定义最多两个参数,针对{{ value1 | filter_multi:value2 }},参数传递为v1=value1,v2=value2
    return  v1 * v2

#2、自定义标签
@register.simple_tag
def my_multi_tag(v1, v2): # 自定义的标签可以定义多个参数
    return v1 * v2


#3、自定义标签扩展之mark_safe
# 注释:我们可以用内置的标签safe来让标签内容有语法意义,如果我们想让自定义标签处理的结果也有语法意义,则不能使用内置标签safe了,需要使用mark_safe,可以实现与内置标签safe同样的功能
from django.utils.safestring import mark_safe

@register.simple_tag
def my_input_tag(id, name):
    res = "<input type='text' id='%s' name='%s' />" % (id, name)
    return mark_safe(res)

4、自定义过滤器或标签必须重新启动django方可生效

5、自定义过滤器或标签的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!--必须先加载存有自定义过滤器和标签的文件-->
{% load my_tags %}

<!--salary的值为10,经过滤器my_multi_filter的处理结果为120-->
{{ salary|my_multi_filter:12 }}

<!--结果为2-->
{% my_multi_tag 1 2 %}

<!--
结果为一个input标签,该表的属性id="inp1" name="username"
注意:input的属性值均为字符串类型,所以my_input_tag后的两个值均为字符串类型
-->
{% my_input_tag "inp1" "username" %} 

</body>
</html>

对比自定义标签与自定义过滤器

#1、自定义过滤器只能传两个参数,而自定义标签却可以传多个参数

#2、过滤器可以用于if判断,而标签不能
{% if salary|my_multi_filter:12 > 200 %}
    <p>优秀</p>
{% else %}
    <p>垃圾</p>
{% endif %}

5 模板的导入和继承

​ 在实际开发中,模板文件彼此之间可能会有大量冗余代码,为此django提供了专门的语法来解决这个问题,主要围绕三种标签的使用:include标签、extends标签、block标签,详解如下

5.1、模板的导入之include标签

作用:在一个模板文件中,引入/重用另外一个模板文件的内容,
{% include '模版名称' %}

"""
将页面的某一个局部当成模块的形式
哪个地方需要就可以直接导入使用即可
"""
{% include 'wasai.html' %}

案例:

可以把广告栏写到专门的文件里advertise.html
<div class="adv">
    <div class="panel panel-default">
        <div class="panel-heading">
            <h3 class="panel-title">Panel title</h3>
        </div>
        <div class="panel-body">
            Panel content
        </div>
    </div>
    <div class="panel panel-danger">
        <div class="panel-heading">
            <h3 class="panel-title">Panel title</h3>
        </div>
        <div class="panel-body">
            Panel content
        </div>
    </div>
    <div class="panel panel-warning">
        <div class="panel-heading">
            <h3 class="panel-title">Panel title</h3>
        </div>
        <div class="panel-body">
            Panel content
        </div>
    </div>
</div>
然后在base.html文件中用include标签引入advertise.html文件的内容
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .header {
            height: 50px;
            width: 100%;
            background-color: black;
        }
        
    </style>
</head>
<body>
<div class="header"></div>
<div class="container">
    <div class="row">
        <div class="col-md-3">
            <!--在base.html引入advertise.html文件的内容-->
            {% include "advertise.html" %}
        </div>
        <div class="col-md-9"></div>
    </div>
</div>
</body>
</html>

5.2、模板的继承\派生之extends标签、block标签

作用:在一个模板文件中,引入/重用另外一个模板文件的内容
{% extends "模版名称" %}
# 也就是说include有的功能extends全都有,但是extends可以搭配一个block标签,用于在继承的基础上定制新的内容
"""
你们有没有见过一些网站:这些网站页面整体都大差不差 只是某一些局部在做变化	
"""
"模版的继承 你自己先选好一个你要想继承的模版页面"
{% extends 'home.html' %}

"继承了之后子页面跟模版页面长的是一模一样的 你需要在模版页面上提前划定可以被修改的区域"
{% block content %}
	模版内容
{% endblock %}

"子页面就可以声明想要修改哪块划定了的区域"
{% block content %}
	子页面内容	
{% endblock %}


"一般情况下模版页面上应该至少有三块可以被修改的区域"
	1.css区域
	2.html区域
	3.js区域
	{% block css %}

	{% endblock %}
---------------
	{% block content %}

	{% endblock %}
---------------
	{% block js %}

	{% endblock %}
    
"每一个子页面就都可以有自己独有的css代码、html代码、js代码"
  
"""
一般情况下 模版的页面上划定的区域越多 那么该模版的扩展性就越高
但是如果太多 那还不如自己直接写
"""

案例

Django模版引擎中最复杂且最强大的部分就是模版继承了。我们以先创建一个基本的“骨架”模版,它包含我们站点中的全部元素,并且可以定义多处blocks ,例如我们创建base.html内容如下
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        {% block title %}自定义title名{% endblock %}
    </title>

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .header {
            height: 50px;
            width: 100%;
            background-color: #919191;
            margin-bottom: 20px;
        }

    </style>

</head>
<body>
<div class="header"></div>

<div class="container">
    <div class="row">
        <div class="col-md-3">
            <div class="list-group">
                {% block sidebar %}
                    <a href="#" class="list-group-item active">服装城</a>
                    <a href="#" class="list-group-item">美妆馆</a>
                    <a href="#" class="list-group-item">超市</a>
                    <a href="#" class="list-group-item">全球购</a>
                    <a href="#" class="list-group-item">闪购</a>
                    <a href="#" class="list-group-item">团购</a>
                {% endblock %}

            </div>
        </div>

        <div class="col-md-9">
            {% block content %}
                base.html页面内容
            {% endblock %}
        </div>
    </div>

</div>

</body>
</html>
模板base.html 定义了一个可以用于两列排版页面的简单HTML骨架。我们新建子模板index.html的主要工作就是继承base.html然后填充/覆盖其内部的blocks。
{% extends "base.html" %}

<!--用新内容完全覆盖了父模板内容-->
{% block title %}
    index页面
{% endblock %}


{% block sidebar %}
    <!--该变量会将父模板中sidebar中原来的内容继承过来,然后我们可以在此基础上新增,否则就是纯粹地覆盖-->
    {{ block.super }}

    <!--在继承父模板内容的基础上新增的标签-->
    <a href="#" class="list-group-item">拍卖</a>
    <a href="#" class="list-group-item">金融</a>
{% endblock %}

{% block content %}
    <!--用新内容完全覆盖了父模板内容-->
    <p>index页面内容</p>
{% endblock %}
我们通过django访问index.html看到内容如下(block标签的内容都完成了替换或更新)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        index页面
    </title>

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .header {
            height: 50px;
            width: 100%;
            background-color: #919191;
            margin-bottom: 20px;
        }

    </style>

</head>
<body>
<div class="header"></div>

<div class="container">
    <div class="row">
        <div class="col-md-3">
            <div class="list-group">
                <!--该变量会将父模板中sidebar中原来的内容继承过来,然后我们可以在此基础上新增,否则就是纯粹地覆盖-->
                <a href="#" class="list-group-item active">服装城</a>
                <a href="#" class="list-group-item">美妆馆</a>
                <a href="#" class="list-group-item">超市</a>
                <a href="#" class="list-group-item">全球购</a>
                <a href="#" class="list-group-item">闪购</a>
                <a href="#" class="list-group-item">团购</a>


                <!--在继承父模板内容的基础上新增的标签-->
                <a href="#" class="list-group-item">拍卖</a>
                <a href="#" class="list-group-item">金融</a>
            </div>
        </div>

        <div class="col-md-9">
            <!--用新内容完全覆盖了父模板内容-->
            <p>index页面内容</p>
        </div>
    </div>

</div>

</body>
</html>

总结与注意

#1、标签extends必须放在首行,base.html中block越多可定制性越强

#2、include仅仅只是完全引用其他模板文件,而extends却可以搭配block在引用的基础上进行扩写

#3、变量{{ block.super }} 可以重用父类的内容,然后在父类基础上增加新内容,而不是完全覆盖

#4、为了提升可读性,我们可以给标签{% endblock %} 起一个名字 。例如:
    {% block content %}
    ...
    {% endblock content %}  
#5、在一个模版中不能出现重名的block标签。
posted @ 2022-03-09 14:14  maju  阅读(24)  评论(0编辑  收藏  举报