模板层

1 模版简介

你可能已经注意到我们在例子视图中返回文本的方式有点特别。 也就是说,HTML被直接硬编码在 Python代码之中。

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

尽管这种技术便于解释视图是如何工作的,但直接将HTML硬编码到你的视图里却并不是一个好主意。 让我们来看一下为什么:

  • 对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。

  • Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。

  • 程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。

基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 我们可以使用 Django的 *模板系统* (Template System)来实现这种模式。

Django模板常用语法:

变量相关:{{ 变量名 }}
逻辑相关:{ %  % }  
 -for循环、if判断、静态文件动态解析、反向解析

 

2 模版语法之变量

views.py

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',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>{{ obj }}</p>    对象不会加括号调用,前端显示为对象内存地址
<p>{{ obj.get_self }}</p>   对象查找方法,不管是绑定给对象、绑定给类还是静态方法,模板语法也会自动加括号调用
<p>{{ obj.get_func }}</p>
<p>{{ obj.get_class }}</p>
#总结:django模板语法内部能自动判断出当前的变量名是否可以加括号调用,如果可以就会自动执行,一般针对类名和函数名

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

 

3 模版之过滤器

在Django的模板语言中,通过使用 过滤器 来改变变量的显示。过滤器就类似于模板语法的内置方法,django内置有60多个过滤器。

过滤器的语法: {{ value|filter_name:参数 }},变量名字|过滤器名称:变量

过滤器最多只能有两个参数,左边的数据是必需的,右边的参数可以有一个或者没有。过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。使用管道符"|"来应用过滤器。

default

如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。例如:

{{ value|default:"nothing"}}

有则返回,无则默认,类似字典的get,有则取,无则赋值

length

返回值的长度。它对字符串和列表都起作用。例如:

{{ value|length }}

filesizeformat

将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB''4.1 MB''102 bytes', 等等)。例如:

{{ value|filesizeformat }}

# 如果 value 是 123456789,输出将会是 117.7 MB。  

date

直接把结构化的时间用模板语法转为格式化时间,如果 value=datetime.datetime.now()

{{ value|date:"Y-m-d H:i:s"}}

slice

支持索引方式切片、支持步长、顾头不顾尾,如果 value=”hello world”

{{ value|slice:"2:-1"}}

safe

Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。

value="<a href="">点击</a>"

{{ value|safe}}

后端转义:写全栈项目时,前端代码不一定非要在前端书写,也可以在后端先写好,通过转义传递给前端

from django.utils.safestring import mark_safe

res = mark_safe('<h1>新新</h1>')

{{ res }} 变量直接传给html就是转义的

truncatechars

如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“…”)结尾。

{{ value|truncatechars:9}}

# 截取9个字符,3个点算一个字符,后面的字符用...展示

truncatewords

截取单词。

{{ value|truncatewords:9}}

# 按照空格截取9个单词,不包含3个点,后面的内容...展示,中文单词同理

cut

移除特定字符

{{ value|cut:' ' }}

# 移除空格 如果value为'i love you',那么将输出'iloveyou'.

join

使用字符串连接列表,例如Python的str.join(list)

{{ l|join:'$' }}  

# 按特定符号拼接

add

加法的拼接

{{ n|add:10 }}  # 结果是n+10

{{ s|add:msg }} # 把两个字符串拼接在一起

其它过滤器(了解)

过滤器 描述 示例
upper 以大写方式输出  
add 给value加上一个数值  
addslashes 单引号加上转义号  
capfirst 第一个字母大写  
center 输出指定长度的字符串,把变量居中  
cut 删除指定字符串  
date 格式化日期  
default 如果值不存在,则使用默认值代替  
default_if_none 如果值为None, 则使用默认值代替  
dictsort 按某字段排序,变量必须是一个dictionary  
dictsortreversed 按某字段倒序排序,变量必须是dictionary  
divisibleby 判断是否可以被数字整除  
escape 按HTML转义,比如将”<”转换为”&lt”  
filesizeformat 增加数字的可读性,转换结果为13KB,89MB,3Bytes等  
first 返回列表的第1个元素,变量必须是一个列表  
floatformat 转换为指定精度的小数,默认保留1位小数  
get_digit 从个位数开始截取指定位置的数字  
join 用指定分隔符连接列表  
length 返回列表中元素的个数或字符串长度  
length_is 检查列表,字符串长度是否符合指定的值  
linebreaks


标签包裹变量

 
linebreaksbr
标签代替换行符
 
linenumbers 为变量中的每一行加上行号  
ljust 输出指定长度的字符串,变量左对齐  
lower 字符串变小写  
make_list 将字符串转换为列表  
pluralize 根据数字确定是否输出英文复数符号  
random 返回列表的随机一项  
removetags 删除字符串中指定的HTML标记  
rjust 输出指定长度的字符串,变量右对齐  
slice 切片操作, 返回列表  
slugify 在字符串中留下减号和下划线,其它符号删除,空格用减号替换  
stringformat 字符串格式化,语法同python  
time 返回日期的时间部分  
timesince 以“到现在为止过了多长时间”显示时间变量  
timeuntil 以“从现在开始到时间变量”还有多长时间显示时间变量  
title 每个单词首字母大写  
truncatewords 将字符串转换为省略表达方式  
truncatewords_html 同上,但保留其中的HTML标签  
urlencode 将字符串中的特殊字符转换为url兼容表达方式  
urlize 将变量字符串中的url由纯文本变为链接  
wordcount 返回变量字符串中的单词数

 

4 模版之标签

标签看起来像是这样的: {% tag %}
标签比变量更加复杂:一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。
一些标签需要开始和结束标签 (例如{% tag %} ...标签 内容 ... {% endtag %})

for标签

遍历每一个元素:

<ul>
{% for user in user_list %}
    <li>{{ user.name }}</li>
{% endfor %}
</ul>

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

遍历一个字典:

{% for foo in dic.keys %}       # 取字典的key,直接循环也是取key
    <p>{{ foo }}</p>
{% endfor %}


{% for foo in d.values %}       # 取字典的值
    <p>{{ foo }}</p>
{% endfor %}

{% for key,val in dic.items %}   # 取字典的键值对,元祖格式,解压赋值
    <p>{{ key }}:{{ val }}</p>
{% endfor %}

注:循环序号可以通过{{forloop}}显示,forloop是模板语法自带的内置变量

Variable Description
forloop.counter 当前循环的索引值(从1开始),类似于计数
forloop.counter0 当前循环的索引值(从0开始)
forloop.revcounter 当前循环的倒序索引值(从1开始),计数倒序
forloop.revcounter0 当前循环的倒序索引值(从0开始)
forloop.first 当前循环是不是第一次循环(布尔值)
forloop.last 当前循环是不是最后一次循环(布尔值)
forloop.parentloop 本层循环的外层循环

for ... empty

for循环的可迭代对象内部没有元素(空列表、空字典),无法循环,不会报错

# for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,可以有所操作。
<ul>
{% for user in user_list %}
    <li>{{ user.name }}</li>
{% empty %}
    <li>空空如也</li>
{% endfor %}
</ul>

if 标签

# {% if %}会对一个变量求值,如果它的值是True(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。

{% if num > 100 or num < 0 %}
    <p>无效</p>
{% elif num > 80 and num < 100 %}
    <p>优秀</p>
{% else %}
    <p>凑活吧</p>
{% endif %}

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

for与if混合使用 

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

with

定义一个中间变量,多用于给一个复杂的变量起别名。注意等号左右不要加空格。with语法内,可以通过as后面的别名快速的使用到前面非常复杂的获取数据的方式。

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

或者

{% with business.employees.count as total %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

csrf_token

这个标签用于跨站请求伪造保护,在页面的form表单里面写上{% csrf_token %}


 

5 自定义标签和过滤器

要实现自定义,必须分三步走

1、在app中创建templatetags模块(模块名只能是templatetags),即在应用下创建一个名字“必须”叫templatetags文件夹

2、在templatetags文件夹内创建任意 .py 文件,如:my_tags.py

3、在该py文件中“必须”先书写下面两句话
		from django import template
		
		register = template.Library()

示例:my_tags.py

from django import template
from django.utils.safestring import mark_safe
 
register = template.Library()   #register的名字是固定的,不可改变

# 自定义过滤器 过滤器最多只能有两个参数
@register.filter(name='sum')
def my_sum(v1, v2):
    return v1 + v2
# html文件中使用
{% load my_tags %}
<p>{{ n|sum:666 }}</p>


# 自定义标签(参数可以有多个) 类似于自定义函数
@register.simple_tag(name='plus')
def index(a,b,c,d):
    return '%s-%s-%s-%s'%(a,b,c,d)
# html文件中使用
{% load my_tags %}
<p>{% plus 'egon' 'tank' 'jason' 'lili' %}</p>

自定义inclusion_tag 

内部原理:先定义一个方法,在一个页面调用该方法,并且可以传值; 该方法会生成一些数据,然后传递给一个局部页面,之后将渲染的结果放到调用inclusion_tag的位置

@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局部页面

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

left_menu.html左侧菜单栏局部页面

# 把数据传递给局部页面进行渲染,这里把数据循环做成列表
<ul>
    {% for foo in data %}
        <li>{{ foo }}</li>
    {% endfor %}
</ul>


# 调用并传参 参数10是传递给left(n) 方法的,渲染后结果最终显示在调用的这个页面
{% load my_tags %}
{% left 10 %}

 

6 模版导入和继承

6.1 模版导入

将页面的某一个局部,如一个form表单、一个列表、面板......当成模板的形式,哪个地方需要就可以直接导入使用即可

语法:{% include '模版名称' %}

如:{% include 'adv.html' %}

# 'adv.html'就是一个局部组件,直接导入,就会把这个html文件的内容塞在导入的这个位置
# 类似于在bootsrtap上拷贝组件放入你的html页面中,只不过这个组件是你自己写的

6.2 模版继承

Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可以让您创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。

通过从下面这个例子开始,可以容易的理解模版继承:

<!DOCTYPE html>
<html lang="en">
<head>
    {% load static %}
    <script src="{% static 'jQuery-3.6.0.js' %}"></script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
    {% block css %}  <!--模板页面划分一个空白的css区域-->

    {% endblock %}
</head>

<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">
        {% load mytag %}         
        {% left_menu username %}
        </div>
        <div class="col-md-9">
        {% block content %}  <!--模板页面划定出一块可以被子页面修改的区域,取一个别名content-->

        {% endblock %}
         </div>
    </div>
</div>

{% block js %}   <!--模板页面划分出一个空白的js区域-->

{% endblock %}    
</body>
</html>

这个模版,我们把它叫作 base.html, 在这个例子中, block 标签定义了三个可以被子模版内容填充的block。 block 告诉模版引擎: 子模版可能会覆盖掉模版中的这些位置。子模版可能看起来是这样的:

{% extends "base.html" %}   <!--让子页面继承模板页面所有的内容-->
 
{% block css %}          <!--子页面写自己专属的css代码-->
    <style>
        子页面css代码
    </style>
{% endblock %}
  
{% block content %}     <!--子页面把模板页面上content区域的代码替换成自己的-->
{% for entry in blog_entries %}
    <h2>{{ entry.title }}</h2>
    <p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

{% block js %}        <!--子页面写自己专属的js代码-->
    <script>
        子页面js代码
    </script>
{% endblock %}

总结:利用模板的继承和修改

1 减少页面的代码重用、代码冗余
2 主页面和各个子页面之间完全互不干扰,各写各的样式和内容
3 模板页面上划定的区域越多,该模板的扩展性就越高,但如果太多,还不如自己分开写

 

posted @ 2022-12-25 23:10  不会钓鱼的猫  阅读(42)  评论(0编辑  收藏  举报