Django 模板层

Django模板层

   模板层的主要任务就是如何将页面呈现给用户,用户能看到的所有东西都是属于模板层进行呈现的。

   HTML文件就是模板,模板是会先经过渲染,再呈现给用户。

   学习模板层主要有以下两点:

   变量相关或处理均使用{{}},即模板变量。

   逻辑相关或处理均使用{%%},即模板标签。

模板变量传值

   其实在聊Django视图层的时候介绍的render()方法中就允许将视图函数中的变量传递给模板。

   模板中使用{{name}}即可完成调用。

def test(request):
    username = "云崖先生"
    return render(request,"test.html",locals())
<body>
    {{username}}
</body>

传值类型

   Python中所有基本数据类型均可以进行模板传递,但是函数、类等可调用类型会出现一些令人意外的情况。

   注意:可调用类型传递时会自动加括号调用,并将返回值进行渲染(如果没有返回值则为None),但是不支持参数的传递。

   后端返回:

def test(request):

    STR = "云崖先生"
    INT = 100
    FLOAT = 11.11
    BOOL = True
    DICT = {"v1":"k1"}
    SET = {"1","2","3"}
    TUPLE = (1,2,3)
    LIST = [1,2,3,4]

    def func():
        return "function"

    class T1(object):
        pass

    return render(request,"test.html",locals())

   模板接收:

<body>

    <p>{{STR}}</p>
    <p>{{INT}}</p>
    <p>{{FLOAT}}</p>
    <p>{{BOOL}}</p>
    <p>{{DICT}}</p>
    <p>{{SET}}</p>
    <p>{{TUPLE}}</p>
    <p>{{LIST}}</p>
    <p>{{func}}</p>  <!-- 不支持参数传递 -->
    <p>{{T1}}</p>  <!-- 实例化出了一个类 -->
    
</body>

   渲染结果:

云崖先生

100

11.11

True

{'v1': 'k1'}

{'1', '2', '3'}

(1, 2, 3)

[1, 2, 3, 4]

function

<app01.views.test.<locals>.T1 object at 0x000001C1E00ACB38>

元素取出

   无论后端的变量是list或者dict,均可使用.语法进行取值。

传值:
def test(request):

    DICT = {"hobby":["basketball","football","volleyball"]}

    return render(request,"test.html",locals())
    

取值:
{{DICT.hobby.2}}

结果:
volleyball

模板变量过滤

   由于后端的函数模板中使用函数无法传递参数,所以提供自带的过滤器进行操作。

   过滤器有非常多的功能,大概有六十多种过滤器提供使用,这里只介绍常用的。

使用示例

   以下举例常用的模板过滤器

过滤器描述
{{v1|length}} 获取v1的长度
{{v1|default:v2}} 如果v1为真,则使用v1,否则使用v2
{{v1|filesizeformat}} v1必须是int类型,对其进行KB、MB、GB、TB等进制之间的转换
{{v1|date:"Y-m-d X"}} v1必须是时间类型,对其进行自定义格式化
{{v1|slice:"start:stop:step"}} v1必须是可通过索引取值的类型,对其进行切片
{{v1|truncatechars:num}} v1必须是可通过索引取值的类型,对其截取指定长度的字符(包含三个.
{{v1|truncatewords:num}} v1必须是字符串类型,对其进行截取特点数量的单词,以空格分隔(不包含.
{{v1|cut:'v2'}} 移除v1中特定的字符v2
{{v1|join:'v2'}} 使用v2对v1进行拼接
{{v1|add:v2}} v1与v2进行相加,如都是int则为加法,如都是str则为拼接
{{v1|safe}} 识别v1中的HTML代码并进行渲染(默认不会进行渲染)

   后端返回:

def test(request):
    import datetime
    v_1 = "12345"
    v_2 = False
    v_3 = 10240
    v_4 = datetime.datetime.now()
    v_5 = ["一","二","三","四","五","六","七"]
    v_6 = "abcdefghijklmn"
    v_7 = "HELLO WORD HELLO DJANGO"
    v_8 = "ABC$$$"
    v_9 = "QWETYU"
    v_10 = 10
    v_11 = "<h1>标题一<h1>"
    return render(request,"test.html",locals())

   模板接收加过滤:

<body>

   <p>{{v_1|length}}</p>
   <p>{{v_2|default:1}}</p>
   <p>{{v_3|filesizeformat}}</p>
   <p>{{v_4|date:"Y-m-d h:m:s"}}</p>
   <p>{{v_5|slice:"1:4:2"}}</p>
   <p>{{v_6|truncatechars:6}}</p> 
   <!-- 上面的: 三个点占三个位置 -->
   <p>{{v_7|truncatewords:3}}</p>
   <!-- 上面的:三个点不占位置 -->
   <p>{{v_8|cut:"$"}}</p>
   <p>{{v_9|join:"---"}}</p>
   <p>{{v_10|add:10}}</p>
   {{v_11|safe}}

</body>

   渲染结果:

   image-20200910223625256

后端渲染

   默认的,后端如果返回HTML代码模板层是不会进行渲染的,这是为了防止XSS攻击。

   但是如果使用mark_safe()进行包裹,则会进行渲染。

  

def test(request):

    from django.utils.safestring import mark_safe # 导入模块
    
    res = "<h1>不会渲染<h1>"
    res_safe = mark_safe("<h1>会渲染<h1>")
    return render(request,"test.html",locals())
<body>

    {{res}}
    {{res_safe}}

</body>

   image-20200910224438551

模板标签分支

基本使用

   模板中支持对{% if %}、{% else %}、{% elif %}进行使用,与Python中逻辑一致。

   注意:判断完成后要使用{% endif %}进行结束判断的标志

<body>

    {% if user %} <!-- 在{%%}的模板标签中,被{{}}套住的变量可以不用写{{}}-->
        <p>有内容</p>
    {% else %}
        <p>无内容</p>
    {% endif %}
    
</body>

模板标签循环

基本使用

   模板中支持{% for %}来进行操作。

   注意:循环完成后要使用{% endfor %}进行结束循环的标志

li = ["A","B","C"]

<body>
    
    {% for row in li %}
        <p> {{row}} </p> <!-- 在{%%}外使用变量,依然要嵌套上{{}} -->
    {% endfor %}
        
</body>

A

B

C

forloop

   {{forloop}}记录了一些循环的信息。

   如下所示:

li = ["A","B","C"]

<body>
    
    {% for row in li %}
       <p> {{forloop}} </p> <!-- 可使用 {{forloop.counter/first/last}} 等 -->
    {% endfor %}
        
</body>

   image-20200910230625903

不可循环

   如果一个迭代对象为空,则不可循环。此时可以使用{% empty %}进行操作

li = []

<body>

    {% for row in li %}
    	<p>不为空</p>
    {% empty %}
    	<p>迭代对象为空</p> <!--执行这里-->
    {% endfor %}

</body>

键值方法

   在循环中,可以对被遍历的对象使用keys、values、items进行操作。

   这与Python中是一样的。

dic = {"name":"云崖","age":"18","gender":"男",}

<body>

    {% for key in dic.keys %}
        <p>{{key}}</p>
    {% endfor %}

    {% for value in dic.values %}
        <p>{{value}}</p>
    {% endfor %}

    {% for kv in dic.items %}
        <p>{{kv}}</p>
    {% endfor %}

</body>

----------------
name

age

gender
----------------
云崖

18

男
----------------
('name', '云崖')

('age', '18')

('gender', '男')

模板标签别名

   可以使用{% with %}配合as对嵌套复杂的单项数据做一个别名。方便后续进行拿出,更推荐与循环进行配套使用。

li = [
	{"username":"Yunya"},
	{"username":"Jack"},
	{"username":"Tom"},
]

----------------

<body>

    {% for row in li %}

        {% with row.username as data %}
                <p>{{data}}</p>
                <!-- 如果不使用别名,则需要写 {{row.username}} 这只是一个示例,实际的嵌套可能比这个复杂的多 -->
        {% endwith %}
        
    {% endfor %}
        
</body>

----------------

Yunya

Jack

Tom

自定义系列

   所有的,自定义都需要完成以下三步骤:

   1.在APP中创建templatetags文件夹,必须叫这个名字

   2.在该文件夹下创建一个py文件,可以是任意名字

   3.导入一个模块,并写一句话

   from django import template

   register = template.Library()

自定义过滤器

   先完成上面三步,再进行过滤器的创建。

   自定义过滤器的作用就是丰富内置的过滤器,完成你的需求。

   注意:过滤器的调用是{{ v1|filter:v2 }},过滤器最多只能传递两个参数,且第一个参数在|左边,第二个参数传递的格式必须是过滤器名字:右边的参数。

   自定义过滤器全步骤:

# app01/templatetags/tags.py

from django import template
register = template.Library()


@register.filter(name="f1")  # 在使用时,使用的是这个名字。函数名可以随便取。注意一定要关键字传参
def f1(left, right):  # left 代表斜杠左边的值,right代表斜杠右边的值
    return left + right

   基本使用:

<body>

    {% load tags %}  <!-- 先导入标签的文件,tags.py -->
    {{1|f1:2}} <!-- 最多两个参数,参数1|过滤器名字:参数2  这是固定格式
        
</body>

-------

3

自定义标签

   自定义标签与自定义过滤器比较相似但是也有两大不同。

   1.支持无限制的参数传递

   2.参数传递方式不同

   注意:标签的调用是{% tag v1 v2 v3 %},这与过滤器有较大的不同。

   先完成上面三步,再进行标签的创建。

# app01/templatetags/tags.py

from django import template
register = template.Library()


@register.simple_tag(name="t1")  # 在使用时,使用的是这个名字。函数名可以随便取。注意一定要关键字传参
def t1(v1, v2, v3):
    return v1 + v2 + v3

   基本使用:

<body>

    {% load tags %} <!-- 先导入标签的文件,tags.py -->
    {% t1 1 2 3%} <!--参数无限制-->
        
</body>

自定义inclusion_tag

   自定义inclusion_tag与上面两个都不太一样,它返回的是一个局部页面,当你的项目中有很多网页中都有相同的一部分网页结构,则使用自定义inclusion_tag来完成。

   还是需要先完成上面三个步骤。

# app01/templatetags/tags.py

from django import template
register = template.Library()

@register.inclusion_tag("common.html") # 去根目录找templates文件夹下的common.html文件
def common_html(username): # 注意,这里调用是函数名,而不是上面指定的名字了
    user = username
    return locals() # 返回当前命名空间中所有变量名
# 项目根目录/templats/common.html

<h1>当前登录:{{user}}</h1> # 可以拿到 common_html 函数中的所有变量名了
<h1>点击注销</h1>
# 被渲染的模板

<body>

    {% load tags %}
    {% common_html "云崖" %} <!---这一处将会替换为指定的页面,即common.html里的内容-->
        
</body>

模板继承引入

模板继承

   模板继承是全栈开发里面Django所提供的非常牛逼的一个功能,它允许你一套HTML模板能被其他模板进行继承并修改一些特定区域的内容。

   image-20200911004024841

# 项目根目录/templats/common.html

{% block left %}
<!-- 这里面可以允许修改,相当于给这块区域起一个名字,left -->

{% endblock %}

============================

# 项目根目录/templats/other.html

{% extends 'common.html' %}
<!-- 继承common.html -->

{% block left %}
<!-- 执行修改 -->

{% endblock %}

   接下来看一下演示代码就知道了。

   这里放一套公用的左右布局页面

# 项目根目录/templats/common.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src='https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js'></script>
    <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'>
    <script src='https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js'
        integrity='sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa'
        crossorigin='anonymous'></script>
    <title>Document</title>
    
    {% block CSS %}
        
    {% endblock %}
        
</head>

<body>

    <header class="navbar navbar-inverse">
        <!-- 头部,不允许改变 -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">Home</a></li>
                <li><a href="#about">About</a></li>
                <li><a href="#contact">Contact</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                        aria-expanded="false">Dropdown <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">Action</a></li>
                        <li><a href="#">Another action</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li role="separator" class="divider"></li>
                        <li class="dropdown-header">Nav header</li>
                        <li><a href="#">Separated link</a></li>
                        <li><a href="#">One more separated link</a></li>
                    </ul>
                </li>
            </ul>
            <ul class="nav navbar-nav navbar-right">
                <li><a href="../navbar/">Default</a></li>
                <li><a href="../navbar-static-top/">Static top</a></li>
                <li class="active"><a href="./">Fixed top <span class="sr-only">(current)</span></a></li>
            </ul>
        </div>
    </header>
    <main class="container-full">

        <div class="row">
            <section class="col-sm-3 col-md-2 sidebar">
                <!-- 左边,允许改变一小块区域 -->
      
                    <ul class="nav nav-sidebar">
                        <li class="active"><a href="#">menu1 <span class="sr-only">(current)</span></a></li>
                        <li><a href="#">menu2</a></li>
                        <li><a href="#">menu3</a></li>
                        <li><a href="#">menu4</a></li>
                    </ul>
                    {% block left %}
                    <!-- 这里面可以允许修改,相当于给这块区域起一个名字,left -->
 
                    {% endblock %}


            </section>

            <section class="col-lg-6">

                {% block right %}
                <!-- 这里面可以允许修改,相当于给这块区域起一个名字right -->
       

                {% endblock %}

            </section>
        </div>

    </main>

</body>

{% block Js %}
    
{% endblock %}

</html>
# 项目根目录/templats/其他页面.html

{% extends 'common.html' %}
<!-- 继承common.html -->

{% block left %}
<!-- 修改内容 -->
<ul class="nav nav-sidebar">
    <li><a href="">menu-div-1</a></li>
    <li><a href="">menu-div-2</a></li>
    <li><a href="">menu-div-3</a></li>
</ul>
{% endblock %}

{% block right %}
<!-- 修改内容 -->
    <h4>Subheading</h4>
    <p>Donec id elit non mi porta gravida at eget metus. Maecenas faucibus mollis interdum.</p>

    <h4>Subheading</h4>
    <p>Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Cras mattis consectetur purus sit amet fermentum.</p>

    <h4>Subheading</h4>
    <p>Maecenas sed diam eget risus varius blandit sit amet non magna.</p>

{% endblock %}

{% block css %}
<!-- 修改CSS -->
{% endblock %}

{% block js %}
<!-- 修改Js -->
{% endblock %}

模板引入

   模板引入是在一个HTML中引入一段公用的HTML代码。

# 项目根目录/templats/common.html

<h1>公用咯<h1>

============================

# 项目根目录/templats/other.html

{include "common.html"}  <!-- 将common.html中代码全部拿过来 -->

posted @ 2020-09-18 14:22  云崖先生  阅读(357)  评论(0编辑  收藏  举报