Django_渲染详解

Django_render

模板语法

模板引擎是一种可以让开发者把服务端数据填充到html网页中完成渲染效果的技术。它实现了把前端代码和服务端代码分离的作用,让项目中的业务逻辑代码和数据表现代码分离,让前端开发者和服务端开发者可以更好的完成协同开发。

静态网页:页面上的数据都是写死的,万年不变

动态网页:页面上的数据是从后端动态获取的(比如后端获取当前时间;后端获取数据库数据然后传递给前端页面)

Django框架中内置了web开发领域非常出名的一个DjangoTemplate模板引擎(DTL)。DTL官方文档

要在django框架中使用模板引擎把视图中的数据更好的展示给客户端,需要完成3个步骤:

  1. 在项目配置文件中指定保存模板文件的模板目录。一般模板目录都是设置在项目根目录或者主应用目录下。

  2. 在视图中基于django提供的渲染函数绑定模板文件和需要展示的数据变量

  3. 在模板目录下创建对应的模板文件,并根据模板引擎内置的模板语法,填写输出视图传递过来的数据。

配置模板目录:在当前项目根目录下创建了模板目录templates. 然后在settings.py, 模板相关配置,找到TEMPLATES配置项,填写DIRS设置模板目录。

render内部本质

image-20220908194058936

def index(request):
	name = "hello world!"
	# 1. 初始化模板,读取模板内容,实例化模板对象
    # get_template会从项目配置中找到模板目录,我们需要填写的参数就是补全模板文件的路径
	template = get_template("index.html")
	# 2. 识别context内容, 和模板内容里面的标记[标签]替换,针对复杂的内容,进行正则的替换
	context = {"name": name}
	content = template.render(context, request) # render中完成了变量替换成变量值的过程,这个过程使用了正则。
	print(content)
	# 3. 通过response响应对象,把替换了数据的模板内容返回给客户端
	return HttpResponse(content)
	# 上面代码的简写,直接使用 django.shortcuts.render
	# return render(request, "index.html",context={"name":name})
    # return render(request,"index3.html", locals())
    # data = {}
    # data["name"] = "xiaoming"
    # data["message"] = "你好!"
    # return render(request,"index3.html", data)
  1. DTL模板文件与普通html文件的区别在哪里?

DTL模板文件是一种带有特殊语法的HTML文件,这个HTML文件可以被Django编译,可以传递参数进去,实现数据动态化。在编译完成后,生成一个普通的HTML文件,然后发送给客户端。

  1. 开发中,我们一般把开发中的文件分2种,分别是静态文件和动态文件。
    • 静态文件,数据保存在当前文件,不需要经过任何处理就可以展示出去。普通html文件,图片,视频,音频等这一类文件叫静态文件。
    • 动态文件,数据并不在当前文件,而是要经过服务端或其他程序进行编译转换才可以展示出去。 编译转换的过程往往就是使用正则或其他技术把文件内部具有特殊格式的变量转换成真实数据。 动态文件,一般数据会保存在第三方存储设备,如数据库中。django的模板文件,就属于动态文件。

模板语法内容

  1. 变量渲染(深度查询、过滤器)

    {{val}}
    {{val|filter_name:参数}}
    
  2. 标签

    {% tag_name %}
    
  3. 嵌套和继承

1.1 深度查询

def index(request):
    name = "ZhangJR"
    age = 22
    is_married = True
    course = ["global education","hongkong economy"]

    regina = {
        "name":"ZhangJR",
        "age": 22,
        "is_married": True,

    }

    class info():
        def __init__(self,name,age):
            self.name = name
            self.age = age
    Info = info("ZhangJR",22)

    Info1 = info("ZhangJR",22)
    Info2 = info("regina", 23)
    Info3 = info("ivnalee", 24)
    Info4 = info("qianniu", 25)
    Infos = [Info1,Info2,Info3,Info4]

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

深度查询在查询属性或者取值的过程都用.作为判断,locals函数代表将视图函数所有的内容全部传送给模板文件渲染

分别通过普通类型,字典类型,类,类的列表进行学习

<h3>深度查询:句点符"."</h3>
<hr>普通类型
<p>姓名:{{name}}</p>
<p>年龄:{{age}}</p>
<p>婚否:{{is_married}}</p>
<p>课程:{{course}}</p>
<p>第一个课程:{{course.0}}</p>

<hr>字典
<p>个人信息:{{regina}}</p>
<p>个人名字:{{regina.name}}</p>
<p>个人年龄:{{regina.age}}</p>

<hr>类对象
<p>类对象名字:{{Info.name}}</p>
<p>类对象年龄:{{Info.age}}</p>

<hr>类列表取值
<p>第二个人的姓名:{{Infos.1.name}}</p>

image-20220908201405331

1.2 内置过滤器

语法:

{{obj|过滤器名称:过滤器参数}}

内置过滤器

过滤器 用法 代码
last 获取列表/元组的最后一个成员 {{list | last}}
first 获取列表/元组的第一个成员 {{list|first}}
length 获取数据的长度 {{list | length}}
default 当变量没有值的情况下, 系统输出默认值, {{str|default:"默认值"}}
safe 让系统不要对内容中的html代码进行实体转义 {{htmlcontent| safe}}
upper 字母转换成大写 {{str | upper}}
lower 字母转换成小写 {{str | lower}}
title 每个单词首字母转换成大写 {{str | title}}
date 日期时间格式转换 `{{ value
cut 从内容中截取掉同样字符的内容 {{content | cut:"hello"}}
list 把内容转换成列表格式 {{content | list}}
add 加法 {{num| add}}
filesizeformat 把文件大小的数值转换成单位表示 {{filesize | filesizeformat}}
join 按指定字符拼接内容 {{list| join("-")}}
random 随机提取某个成员 {list | random}}
slice 按切片提取成员 {{list | slice:":-2"}}
truncatechars 按字符长度截取内容 {{content | truncatechars:30}}
truncatewords 按单词长度截取内容 同上
Infos = [Info1.name,Info2.name,Info3.name,Info4.name]
<p>信息的最后一个人的姓名:{{Infos | last}}</p>
<p>信息的第一个人的姓名:{{Infos | first}}</p>
<p>共有{{Infos | length}}个人</p>

image-20220908202840812

  • 默认值

    如果渲染的值为空,可以添加默认渲染值,效果如图

    Name= ""
    
    姓名{{Name}}
    

    image-20220908203046383

如果变成<p>姓名 {{Name|default:"regina"}}</p>(default后面不能有空格)

image-20220908203351068

  • 日期转换

    now = datetime.datetime.now()
    <p>当前时间默认格式:{{now}}</p>
    <p>格式化日期:{{now|date:"Y-m-d"}}</p>
    <p>格式化日期2:{{now|date:"D/d/m/Y"}}</p>
    

    image-20220908204007197

  • 文件大小

    filesizeformat过滤器自动会识别字节大小进行单位的选择

     file1 = 717
      file2 = 127717
      file3 = 1271221
      file4 = 1155178797
    <p>文件大小:{{file1|filesizeformat}}</p>
    <p>文件大小:{{file2|filesizeformat}}</p>
    <p>文件大小:{{file3|filesizeformat}}</p>
    <p>文件大小:{{file4|filesizeformat}}</p>
    

    image-20220908204656545

  • 按字符截断

    str = "ABCDEFGHIJK"
    <p>content: {{str | truncatechars:6}}</p>
    

    image-20220908205214957

    这个...默认算作了一个字符

  • safe过滤

    如果我们输入的是一段带有标签的html语言,类似于

        link = "<a href='www.gs.cuhk.edu.hk'>cuhk</a>"
        script = "<script>alert('regina')</script>"
    

    如果正常传输,那网页上得到的则是

    image-20220909102044206

    并不是一个我们想要的效果,因为默认过滤器会对一些字符进行转义,打开页面源码就会发现这一问题

    image-20220909102146093

    如果我们加上safe过滤器

    <p>link:{{link| safe}}</p>
    <p>script:{{script|safe}}</p>
    

    image-20220909102248840

    image-20220909102306168

    image-20220909102323379

    代码也没有进行任何的修改

1.3 自定义过滤器

虽然官方已经提供了许多内置的过滤器给开发者,但是很明显,还是会有存在不足的时候。例如:希望输出用户的手机号码时, 13912345678 ---->> 139*****678,这时我们就需要自定义过滤器。要声明自定义过滤器并且能在模板中正常使用,需要完成2个前置的工作:

  1. 当前使用和声明过滤器的子应用必须在setting.py配置文件中的INSTALLED_APPS中注册了!!!

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
    ]
    

    这里面并没有添加自定义的app进去,现在我们找到如下文件,并在上述代码添加regina.apps.ReginaConfig

    image-20220914094726582

    1. 自定义过滤器函数必须被 template.register进行装饰使用,而且过滤器函数所在的模块必须在templatetags包里面保存

      在home子应用下创建templatetags包[必须包含__init__.py], 在包目录下创建任意py文件

      image-20220914095239114

      myfilters的名字是自定义的,这里就是存储过滤函数,并需要在最开始加上一些头文件

      from django import template
      register = template.Library()
      
      @register.filter("mobile")
      def mobile(number):
          return number[:3]+"****"+number[-3:]
      
    2. 在需要使用的模板文件中顶部使用load标签加载过滤器文件my_filters.py并调用自定义过滤器{% load myfilters %}

      过滤的函数要和myfilters文件里面的函数是同名的

      最终当输入一个手机号时,得到就是一个隐藏后的手机号

      image-20220914101648307

2.1 标签

{% tagname %}

if标签

is_married = True
{% if is_married == True %}
    <p>张女士已婚</p>
{% else %}
    <p>张女士未婚</p>
{% endif %}

这里需要注意的几个点:

  1. {% if condition %}这里的condition和if都需要和周围的%空开一格
  2. {% if is_married == true %}这里要用双等号
  3. 最后一定要加

image-20220914155633365

<div>
    {% if age < 20 %}
        <p>太小</p>
    {% elif age < 22 %}
        <p>还行</p>
    {% else %}
        <p>正好</p>
    {% endif %}
</div>

for循环

<ul>
    {% for name in course %}
    <li>{{ name }}</li>
    {% endfor %}
</ul>

image-20220914161141722

<table width="800" border="1" align="center">
    <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>年龄</th>
    </tr>

{% for info in Infos %}
    <tr align="center">
        <td>{{ forloop.counter }}</td>
        <td>{{ info.name }}</td>
        <td>{{ info.age }}</td>
    </tr>
{% endfor %}
</table>

image-20220914162916027

还可以结合if判断

{% for info in Infos %}
    {% if forloop.first %}
        <tr align="center" class="pink">
        <td>{{ forloop.counter }}</td>
        <td>{{ info.name }}</td>
        <td>{{ info.age }}</td>
    </tr>
    {% else %}
        <tr align="center">
            <td>{{ forloop.counter }}</td>
            <td>{{ info.name }}</td>
            <td>{{ info.age }}</td>
        </tr>
    {% endif %}

{% endfor %}

image-20220914163318861

或者还可以

    {% elif info.age > 23 %}
        <tr align="center" >
            <td>{{ forloop.counter }}</td>
            <td class="blue">{{ info.name }}</td>
            <td>{{ info.age }}</td>
        </tr>

image-20220914164050224

循环中, 模板引擎提供的forloop对象,用于给开发者获取循环次数或者判断循环过程的.

属性 描述
forloop.counter 显示循环的次数,从1开始
forloop.counter0 显示循环的次数,从0开始
forloop.revcounter0 倒数显示循环的次数,从0开始
forloop.revcounter 倒数显示循环的次数,从1开始
forloop.first 判断如果本次是循环的第一次,则结果为True
forloop.last 判断如果本次是循环的最后一次,则结果为True
forloop.parentloop 在嵌套循环中,指向当前循环的上级循环

3 嵌套继承

传统的模板分离技术,依靠{% include "模板文件名"%}实现,这种方式,虽然达到了页面代码复用的效果,但是由此也会带来大量的碎片化模板,导致维护模板的成本上升.因此, Django框架中除了提供这种模板分离技术以外,还并行的提供了 模板继承给开发者.

{% include "模板文件名"%}  # 模板嵌入
{% extends "base.html" %} # 模板继承 

3.1 include

假设现在有两个页面公用一个同样式的东西,即页面底部有一个广告~

image-20220914165809050

在每一个页面里都添加一句

{% include "same.html" %}

image-20220914170307750

image-20220914170328853

此时两个页面就同时拥有了这一元素

3.2 extends继承

因为一个页面继承的东西可能有很多,而且有很多的固定框架,不可能每一个都用include进行继承,此时就需要extends关键字,首先我们来用bootstrap学习一下,然后在我们需要自己渲染的位置上添加区块

 <div class="col-md-10">
   <div class="theme-showcase" role="main">
     {% block content %}
     <p>hello world!</p>
     {% endblock %}
   </div>
</div>

其中content是自定义的名字,在模板文件里也同样要用这个名字,然后在其他文件里进行导入这个文件

{% extends "same.html" %}
.....
{% block content %}
原来渲染的内容
{% endblock %}

被extends的文件在未使用时是这样的

image-20220915000327456

我们看到有两个块是要传递给模板文件渲染的,index.htmltest.html同时继承了这一文件,效果如图

image-20220915000446635

image-20220915000506977

  • 如果你在模版中使用 {% extends %} 标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作。
  • 在base模版中设置越多的 {% block %} 标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。
  • 为了更好的可读性,你也可以给你的 {% endblock %} 标签一个 名字 。例如:{``% block content``%``}``...``{``% endblock content``%``},在大型模版中,这个方法帮你清楚的看到哪一个  {% block %} 标签被关闭了。
  • 不能在一个模版中定义多个相同名字的 block 标签。

在最开始的same页面中,我们是添加了一句话的,虽然这句话并不重要,但是如果我们继承的时候也想要这句话,就需要把语句改为

{% block content %}
{{ block.super }}
....

image-20220915001014323

此时原来same.html里面的block内容也可以得到继承

posted @ 2022-09-15 00:13  ivanlee717  阅读(662)  评论(1编辑  收藏  举报