10-模板层

楔子

将前端页面和Python 的代码分离是一种的开发模式。 为此 Django专门提供了模板系统 (Template System,即模板层)来实现这种模式。

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

存放于 templates 目录下的 html文件称之为模板文件,要返回的 html页面中的数据是动态的,那么必须在 html页面中嵌入变量,这便用到了Django 的模板语法。

模板语法

1、跟变量相关的都使用双括号 {{}}
2、跟逻辑相关的都使用 {%  %}
3、模板文件中取值一律使用点语法 
4、在模板文件里面的函数和类,不用加括号,会自动加括号调用, 不能传递参数

本质上:在HTML中写一些占位符,由数据对这些占位符替换和处理,原始数据怎么样,如果不做处理那么网页显示的数据就会是怎么样。

image-20240228160405573

python中的列表取值,通过list[index],Django的模板语法取值字典的索引,通过点去操作

image-20240228160640712

列表循环操作

image-20240228161132256

字典循环操作

image-20240228162535884

image-20240228163129334

条件判断

  • {% if 条件 %} 条件为真时 if 的子句才会生效,条件也可以是一个变量。
  • if 会对变量进行求值,在变量值为空、或者视图没有为其传值的情况下均为 False。
  • if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not 判断。

image-20240228164036020

image-20240228164420354

基于模板语法和爬虫的小练习

image-20240228170643664

案例演示

# 视图层
def to_html(request):
    name = '小满'
    age = 3
    score = 99.00
    eva_score = 99.87
    hobby = ['逃课', '欺负同学']
    data = {'name': '大乔', 'hobby': ['欺负小满', '抢小满人头']}
    is_single = True
    is_beautiful = False
    dislike = {'老夫子', '红buff又没了'}
    like = ('阿珂', '海月')

    def foo():
        return '你是年少的欢喜'

    class Hero:
        name = '阿珂'

        @property
        def hobby(self):
            return '欺负小满'

    ak = Hero()

    return render(request, 'app1/jinjia2.html', locals())
<!-- 前端网页 -->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hi~ o(* ̄▽ ̄*)ブ</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link rel="stylesheet" href="{% static 'app1/css/jinjia2.css' %}">
    <link href="https://fonts.googleapis.com/css2?family=Zhi+Mang+Xing&display=swap" rel="stylesheet">
    <script src="{% static 'app1/js/fontawesome.js' %}"></script>
</head>
<body>
    <p class="title">测试Jinjia2模板语法</p>
    <div id="left">
        <p>字符串<i class="fa-solid fa-arrow-right"></i>{{ name }}</p>
        <p>整数<i class="fa-solid fa-arrow-right"></i>{{ age }}</p>
        <p>浮点型(全是0)<i class="fa-solid fa-arrow-right"></i>{{ score }}</p>
        <p>浮点型(常规)<i class="fa-solid fa-arrow-right"></i>{{ eva_score }}</p>
        <p>列表<i class="fa-solid fa-arrow-right"></i>{{ hobby }}</p>
        <p>字典<i class="fa-solid fa-arrow-right"></i>{{ data }}</p>
        <p>布尔<i class="fa-solid fa-arrow-right"></i>{{ is_single }}</p>
        <p>布尔<i class="fa-solid fa-arrow-right"></i>{{ is_beautiful }}</p>
        <p>集合<i class="fa-solid fa-arrow-right"></i>{{ dislike }}</p>
        <p>元组<i class="fa-solid fa-arrow-right"></i>{{ like }}</p>
    </div>

    <div id="middle">
        <p class="on-right">遍历列表</p>
        {% for item in hobby %}
            <p>{{ item }}</p>
        {% endfor %}
        <hr>
        <p  class="on-right">遍历字典</p>
        {% for item in data %}
            <p>{{ item }}</p>
        {% endfor %}
        <hr>
        <p  class="on-right">遍历集合</p>
        {% for item in dislike %}
            <p>{{ item }}</p>
        {% endfor %}
        <hr>
        <p  class="on-right">遍历元组</p>
        {% for item in like %}
            <p>{{ item }}</p>
        {% endfor %}

    </div>

    <div id="right">
        <p>函数<i class="fa-solid fa-arrow-right"></i>{{ foo }}</p>
        <p>类<i class="fa-solid fa-arrow-right"></i>{{ ak }}</p>
        <p>类(通过.去获得属性)<i class="fa-solid fa-arrow-right"></i>{{ ak.name }}</p>
        <p>类(通过.去获得方法)<i class="fa-solid fa-arrow-right"></i>{{ ak.hobby }}</p>
    </div>

    <div class="outer"></div>
    <p class="title">小满三岁啦</p>
    <img src="{% static 'app1/img/小满三岁啦.jpg' %}" alt="">
</body>
</html>
/* css */
body {
    background-image: url(https://i.pinimg.com/564x/07/b0/45/07b045db2a74d50fbdea42539cc4ccbf.jpg);
}

p {
    padding: 5px 0 0 15px;
    font-size: 18px;
}

p.title {
    font:700 4em 'Zhi Mang Xing';
    text-align: center;
    color: tomato;
    text-decoration: underline 2px wavy green;
    clear: both;
}

i {
    padding: 0 10px 1px  10px;
    display: inline-block;
    color: red;
}

#left, #middle, #right {
    float: left;
    border: 1px solid tomato;
    margin: 20px;
    width: 30%;
}

#middle > hr {
    border: 1px solid tomato;
}

#middle > .on-left {
    float: left;
}

#middle > .on-right {
    float: right;
    padding: 10px;
}

img {
    width: 359px;
    float: right;
    position: relative;
    top: -440px;
    right: 177px;
}

.outer {
    width: 567px;
    height: 300px;
    float: right;
    position: relative;
    top: -17px;
    right:82px;
    border: 1px solid tomato;
}

image-20240302013735239

模板语法之过滤器

类似于python里面的内置方法

语法 {{变量|过滤器:参数}}

语法 说明 例子
default 如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值 {{ value|default:"nothing"}}
length 返回值的长度,对字符串和列表都起作用。 {{ value|length }}
filesizeformat 将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)。 {{ value|filesizeformat }}
date 时间格式化,如果now=datetime.datetime.now(),那么结果就算2024-03-02 01-03-32 {{ now|date:'Y-m-d H-m-s'}}
slice 切片,用法和python一样,支持步长,顾头不顾尾,比如字符串 text='你是年少的欢喜'执行后面的将得到是年少 {{ text|slice:"2:5"}}
truncatechars 如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“…”)结尾。 {{ sentence|truncatechars:"30"}}
safe 添加safe告诉Django不必转义 {{p_tag|safe}}
join 字符串的拼接,被拼接的需要是一个序列,注意没用空格,hobby = ['逃课', '欺负同学']使用例子后的结果就算逃课.欺负同学 {{ hobby|join:"." }}
add 加法,很容易理解score=99,执行例子的结果就是109 {{ score|add:10 }}

filesizeformat 案例

# 视图层函数内部
import os
from pathlib import Path

path = Path('app1/static/app1/img/小满三岁啦.jpg').absolute()
size_of_img = os.path.getsize(path)
<!-- 前端 -->
<div class="outer"></div>
<p class="title">小满三岁啦</p>
<img src="{% static 'app1/img/小满三岁啦.jpg' %}" alt="">
<p>路径地址为:{{ path }}</p>
<p>原始大小为:{{ size_of_img }} (单位字节)</p>
<p>格式化后的大小为:{{ size_of_img|filesizeformat }}</p>
# 结果
路径地址为:E:\djangoProject\mysite\app1\static\app1\img\小满三岁啦.jpg
原始大小为:87879 (单位字节)
格式化后的大小为:85.8 KB

truncatechars 案例

sentence = '我在爱情上的愚钝就像是门窗紧闭的屋子,' \
               '虽然爱情的脚步在屋前走过去又走过来,我也听到了,' \
               '可是我觉得那是路过的脚步,那是走向别人的脚步。' \
               '直到有一天,这个脚步停留在这里。'
{{ sentence|truncatechars:"30"}}
# 原字符串
我在爱情上的愚钝就像是门窗紧闭的屋子,虽然爱情的脚步在屋前走过去又走过来,我也听到了,可是我觉得那是路过的脚步,那是走向别人的脚步。直到有一天,这个脚步停留在这里。

# 截断后
我在爱情上的愚钝就像是门窗紧闭的屋子,虽然爱情的脚步在屋前…

safe

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

 p_tag = '<p>一屋两人三餐四季</p>'
{{ p_tag }}
{{ p_tag|safe }}
# 结果
<p>一屋两人三餐四季</p>
一屋两人三餐四季

make_safe

后端转义

from django.utils.safestring import mark_safe

p_tag = '<p>一屋两人三餐四季</p>'
p_tag = mark_safe(p_tag)
{{ p_tag }}
一屋两人三餐四季

其它过滤器(了解即可)

过滤器 描述
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 返回变量字符串中的单词数

模板语法之循环序号

循环序号可以通过{{forloop}}显示

image-20240302023401082

image-20240302023231316

常用标签之 with 标签

with标签用来为一个复杂的变量名起别名,如果变量的值来自于数据库

在起别名后只需要使用别名即可,无需每次都向数据库发送请求来重新获取变量的值

{% with li.1.upper as v %}
    {{ v }}
{% endwith %}

常用标签之 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请求,否则拒绝,以此来确定客户端的身份

自定义标签之simple_tag

参考链接:https://www.liujiangblog.com/course/django/150

  1. 在当前app下建立一个名称叫做templatetags的文件夹,(只能叫这个名称),然后再给他新建一个__init__.py里面可以不用写任何东西。
  2. 在新建的文件夹下面,新建一个自定义过滤器的名称,比如my_tag.py
  3. 后续的后端的逻辑代码都是放在这个py文件里面写的

image-20240304161453708

自定义标签之inclusion_tag

# 自定义的py文件 位于tamplatetags文件夹下

# 如何制作inclusion_tag
# 1. 在当前APP下或根目录下创建一个文件夹 templatetags
# 2. 在当前文件夹下创建一个 py 文件 (名字随意)
# 3. 在py文件内部 首先要导入
from django import template
from blog import models

# 4. 创建一个注册对象  这里只能叫 register
register = template.Library()


# 5. 创建一个函数
# 函数的头顶上要用装饰器注册
# 参数是指定目录下的前端模板文件
@register.inclusion_tag('tags/adv.html')
def home_adv():
    # 6. 声明需要在前端文件使用的数据
    adv_data = models.Adv.objects.all().order_by('pk')
    # 返回当前函数的名称空间
    return locals()
<!-- 要渲染的模板文件 -->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div class="col-md-2">
            <div class="thumbnail">
                <img src="{% static 'img/代码猫.jpg' %}" alt="代码猫">
                <h3>Opps~</h3>
                <p class="code-secret">写代码不加班的秘密竟然是...</p>
                <br>
                <p>
                    <a href="https://api.52vmy.cn/api/wl/moyu" target="_blank" class="btn btn-primary btn-block" role="button">点我了解</a>
                    <button class="btn btn-danger btn-block">关闭广告</button>
                </p>
            </div>
        </div>
</body>
</html>

{# 使用inclusion_tag  #}
{# 第一步 加载 load py文件名 #}
{% load CommonInclusionTags  %}
{# 第二步 使用 就是加载的py文件下面定义的函数名 #}
{% home_adv %}

模板的继承

开发中,模板文件彼此之间可能会有大量冗余代码,为此Django 提供了专门的语法来解决这个问题,主要围绕三种标签的使用:

  1. include 标签
  2. extends 标签
  3. block 标签

模板的导入之 include 标签

作用: 在一个模板文件中,引入/重用另外一个模板文件的内容

{% include '模版名称' %}

image-20240302024924855

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

作用: 在一个模板文件中,引入/重用另外一个模板文件的内容

也就是说include 有的功能extends 全都有,但是extends可以搭配一个block标签,用于在继承的基础上定制新的内容

Django模版引擎中最复杂且最强大的部分就是模版继承了。

创建一个基本的“骨架”模版,它包含站点中的全部元素,并且可以定义多处blocks 。(block标签的内容替换或更新)

image-20240302030359288

image-20240302030456089

总结与注意

标签 extends 必须放在首行,html 文件中 block 越多可定制性越强

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

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

在一个模版中不能出现重名的 block 标签。

为了提升可读性,我们可以给标签 {% endblock %} 起一个名字例如: {% block content %} ...

posted @ 2024-03-23 00:49  小满三岁啦  阅读(5)  评论(0编辑  收藏  举报