TOC

模版技术

    Django框架在进行路由匹配之后,会映射到一个可调用对象,一般为视图函数,在处理函数中,有使用过HttpResponse对象和JsonResponse对象,这些东西相对于来说,比较简单,要么给客户端返回一个HTML文档,要么客户端返回一个JSON数据;
    对于这种响应报文的主体部分,有两种用法,第一种是完全在Django中不实现任何HTML页面,纯粹给客户端返回一个JSON数据,将Django作为一个数据源的角色,这是一种方式,还有一种方式,就是在视图处理函数里面,找到一个HTML文档,然后返回给客户端使浏览器进行渲染;
    那么对于模版来讲,它说的就是上面说的第二种方式,即,返回一个HTML文档,当然,加入了模版这个元素之后,这个HTML文档,就不是一个简单的HTML文档了,对于这个HTML文档里面有一部分是一些占位符,类似我们的字符串格式化一样,它在Django里面叫做,模版字符,如下;
# views.py
class Home(View):
    def get(self, request):
        data = {"name": "cce", "age": 10}
        template_file="<html><body><h1>My Name Is {name} and Age Is {age}</h1></body></html>"
        response_body = template_file.format(**data)
        return HttpResponse(response_body)
# urls.py
urlpatterns = [
    path('index/', Home.as_view()),
]

模版技术

    可以看到上面就是通过字符串格式化实现的模版技术,其实如果希望更加明显一点,我们可以将变量template_file的内容设置成一个HTML文件,这样这个HTML文件就成为了,我们所谓的"模版文件",然后使用open()方法将这个"模版文件"读取出来,随后通过传入的data数据,进行HTML文档的动态的包装,从而形成一个完整的HTML文档,这样更符合模版的特性,只不过上述这样实现只是为了笔记是美观性;
    其实这就是模版技术,但是上述,我们仅仅只能实现,基础的占位符替换,那么如果我们期望更加复杂的处理,比如在模版文件里面对传入进来的数据进行判断、进行循环等等,使用这种字符串格式化方式是不行的,那么这个时候,就形成了一种更加高级的技术了,即,模版技术,但是本质上就是上面这样,通过替换的方式,将内容填充到占位符;
    常见的模版技术有,JSP、ASP 、PHP和.NET,那么在Django中,也提供了模版技术的支持,它们实际上就是在HTML文档里面写入一些模版字符串,然后在正式构建Response报文之前,将模版字符串替换成真正的的数据,其实都是这么一个实现思路;

Django模版

    对于Django的模版,就底层实现的方式,和上面没什么区别,只不过对于Django模版来讲,将模版字符串放在一个HTML文档里面,从而形成HTML模版,然后在返回响应报文之前,将这个HTML模版进行格式化,将HTML模版里面的模版字符串替换成我们期望的数据,形成一个HTML完整的文档,这一套逻辑,进行了封装,封装成一种模版语言,同时也支持了更多的特性,比如常见的字符判断、循环、切割等技术,这就是Django中的模版技术;
Django模版基础配置
    在Django中提供了一套模版语言技术,称之为Django Template Language(DTL),对于Django的模版技术,分几步,第一步加载HTML模版文件,然后进行占位符替换,最后封装成一个HTTP的Response对象;
    那么对于Django中的模版来讲,一般在创建一个Django项目时,会提供一些默认设置,它规定了模版应该如何解析,如何检索等,其实我们只需要关注下面前三个配置即可,如下;
# settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
# 解释
BACKEND:它为一个完整的路径,主要用途为指定Django使用的模版引擎,主要有两种,如下; 
    django.template.backends.django.DjangoTemplates
    django.template.backends.jinja2.Jinja2
DIRS:这是一个列表,在这个列表中可以存放所有的模板路径。以后在视图中使用render()或者render_to_string()渲染模板的时候,就会在这个列表的路径中查找模板。即Django会默认的在这个列表中配置的路径下去寻找模板;
APP_DIRS:默认值为True,这个设置为True,会在INSTALLED_APPS配置中的所有的app下的templates文件夹中查找模板,这也就是为什么在创建一个App后,需要在INSTALLED_APPS中注册这个app的原因之一;
OPTIONS:传递给该模板引擎(backend)的其他参数,不同的引擎,可用的参数不一样,无需详细了解;
模版文件基础
    对于Django来讲,默认存储模版文件的目录名称为"templates",那么根据上面配置的解释来看,一般情况下,我们只需要在App目录下面创建一个名为"templates"的目录存储模版文件即可,因为TEMPLATES配置的APP_DIRS的值默认为True,无需修改任何配置,如下;
[cce@doorta ~]# tree /usr/local/Project/blog 
/usr/local/Project/blog
├── blog
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
└── user
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations
    │   ├── __init__.py
    ├── models.py
    ├── templates  # 模版文件夹
    │   └── index.html  # 模版文件
    ├── tests.py
    └── views.py
基础模版语言
    为了能更好的演示模版的基础流程,首先先来看下Django模版语言的基础占位符替换语法,如果只是做字符替换其实很简单,它和我们的字符串很类似,在模版文件中,我们的变量可以使用双大括号包裹一个变量名来表示,如{{ name }},这就是Django模版语言中占位符的表示方法,如下;
# 模版文件
[cce@doorta ~]# cat /usr/local/Project/blog/user/templates/index.html
{{ name }}%
模版渲染
    Django模版的解析流程第一步就是加载模版,即,在模版目录下面检索到指定路径或者名称的模版,Django提供了一个loader类,该类在django.template包下,该类下面提供了一个get_template()方法,该方法首先会找到这个模版的位置,然后将这个模版加载;
    同时get_template()方法返回一个django.template.backends.django.Template对象,该对象所属的类就是提供模版渲染的类,它下面有一个render方法,该方法主要就是用来进行模版渲染的,它的返回值就是替换完成之后的完整的HTML文档,如下;
# /usr/local/Project/blog/user/templates/index.html
My Name Is {{ name }} and Age Is {{ age }}

# views.py
class Home(View):
    def get(self, request):
        data = {"name": "cce", "age": 18}
        template_file_content = get_template("index.html")
        return HttpResponse(template_file_content.render(data))

    可以看到,render()方法,将这个模版文件进行渲染之后,成功的进行了占位符替换完成,返回了一个替换完成之后的一个完整的HTML文档字符串,然后使用HttpResponse对象对HTML文档字符串封装成一个Response对象,然后返回给客户端,浏览器也成功的显示了一个在后端进行渲染完成的HTML文档;
模版渲染快捷方法
    因为django.template.backends.django.Template下面render方法,返回的是一个django.utils.safestring.SafeText对象,通过源码可以看到,该对象就是一个str方法的子类,说白了,返回的就是经过占位符替换完成之后一个完整的HTML文档格式的字符串,所以它并非是一个封装后的HTTP Response对象,因此,我们还是需要使用HttpResponse类将其封装成一个Response对象;
class SafeText(str, SafeData):pass
    那么Django为了方便开发者,提供了一个更加方便的方法,该方法在django.shortcuts模块下,名为render,源码如下,将占位符替换完成之后的完整的HTML文档字符串使用HttpResponse类封装成一个HTTP对象;
def render(request, template_name, context=None, content_type=None, status=None, using=None):
    content = loader.render_to_string(template_name, context, request, using=using)
    return HttpResponse(content, content_type, status)
    因此,我们就可以直接使用该方法进行模版字符串的渲染,和HTTP响应报文的封装,这个render比get_template()方法返回的Template对象下的rander()更加的方便,如下;
# /usr/local/Project/blog/user/templates/index.html
My Name Is {{ name }} and Age Is {{ age }}

# views.py
class Home(View):
    def get(self, request):
        data = {"name": "cce", "age": 18}
        return render(request, 'index.html', data, status=201)

  • 注意:此外,在django.shortcuts模块下,还提供了一个render_to_response()方法,它render()方法其实差不多,只不过,如果我们希望在模版中使用我们request对象里面的一些属性时,我们可以直接使用render()方法,当然,如果只做占位符替换,我们也可以使用render_to_response()方法来实现;

Django模版语言

    Django模版语言,即DTL语法,在上面了解了模版技术的底层实现之后,开始了解模版语言,模版语言,主要有三类,模版基本数据类型(变量、列表、字典)、模版标签和模版过滤器;
    那么为了方便实验,也为了笔记的美观性,我们就将视图处理函数统一,后面的所有的模版语言基础数据类型的实验,全部都适用于如下视图处理函数;
# views.py
from django.shortcuts import render_to_response as render
import datetime

class Home(View):
    def get(self, request):
        data = {
            "name": "cce",
            "members": ["cce", "cfj", "dxf"],
            "parents": [
                {"name": "cfj"},
                {"name", "dxf"}
            ],
            "info": {
                "hobby": ["游泳", "打球"],
                "birthday": datetime.datetime(1996, 4, 26, 00, 00, 00)
            }
        }
        return render('index.html', data, status=201)

# urls.py
urlpatterns = [
    path('', Home.as_view()),
]
模版变量
    对于Django模版语言中的变量来说,它以两个大括号包裹一个变量名称的方式表示变量语法,如:{{ name }},如下;
[cce@doorta ~]# cat /usr/local/Project/blog/user/templates/index.html
基础变量:{{ name }}
[cce@doorta ~]# curl http://127.0.0.1:8000/
基础变量:cce
模版列表
    在Django模版中,如果我们期望能够访问一个列表中的元素,和Python中的列表是一样的,都是通过索引的方式去查找,但是,我们需要注意的是,在Django模版语言中,索引号,是以一个属性的方式存在,所以在Django的模版语言中,如果我们需要访问一个列表中指定索引号的数值时,语法格式为,"list_name.index";
  • 注意:可以看到下面的结果中,很多"&#"这样的特殊字符,这是因为模版语言将我们的冒号转换了;
[cce@doorta ~]# cat /usr/local/Project/blog/user/templates/index.html
基础变量:{{ parents.0 }}% 
[cce@doorta ~]# curl http://127.0.0.1:8000/                          
基础变量:{&#39;name&#39;: &#39;cfj&#39;}%  
  • 注意:需要注意,index默认从0开始,且不可为负数;
模版字典
    因为Django模版,对模版语言做了处理,在模版中,不能使用原生的字段语法获取字典中的键值对,它将字典封装成了一个对象,如果我们需要在模版中需要获取字典中的值时,值需要使用对象的方式访问即可,无法通过字典的方式来访问,如下;
  • 注意:可以看到下面的结果中,很多"&#"这样的特殊字符,这是因为模版语言将我们的冒号转换了;
[cce@doorta ~]# cat /usr/local/Project/blog/user/templates/index.html
基础变量:{{ info.birthday }}
[cce@doorta ~]# curl http://127.0.0.1:8000/             
基础变量:1996年4月26日 00:00
    如果我们希望访问到字典里面所有的key,那么我们可以直接访问这个字典对象的keys属性,但是需要注意一点,后面没有括号"()",在模版语言中,括号都去掉了,如下;
[cce@doorta ~]# cat /usr/local/Project/blog/user/templates/index.html
基础变量:{{ info.keys }}%
[cce@doorta ~]# curl http://127.0.0.1:8000/                          
基础变量:dict_keys([&#39;birthday&#39;, &#39;hobby&#39;])%
    如果希望访问到字典里面所有的values,那么我们可以直接访问这个字典对象的values属性,但是需要注意一点,后面没有括号"()",在模版语言中,括号都去掉了,如下;
[cce@doorta ~]# cat /usr/local/Project/blog/user/templates/index.html
基础变量:{{ info.values }}%
[cce@doorta ~]# curl http://127.0.0.1:8000/                          
基础变量:dict_values([datetime.datetime(1996, 4, 26, 0, 0), [&#39;游泳&#39;, &#39;打球&#39;]])%
    如果希望访问到字典里面的keys和values的组合,那么我们可以直接访问这个字典对象的items属性,但是需要注意一点,后面没有括号"()",在模版语言中,括号都去掉了,如下;
[cce@doorta ~]# cat /usr/local/Project/blog/user/templates/index.html
基础变量:{{ info.items }}%
[cce@doorta ~]# curl http://127.0.0.1:8000/                          
基础变量:dict_items([(&#39;birthday&#39;, datetime.datetime(1996, 4, 26, 0, 0)), (&#39;hobby&#39;, [&#39;游泳&#39;, &#39;打球&#39;])])% 
模版字典查找顺序
    默认情况下,Django模版语言对于字典的查找方式很特殊,首先在Django模版中,只允许使用类似Python对象的方式来访问,假设我们需要访问字典的keys,即使{{ info.keys }},那么它首先会访问这个info对象下面是否有keys属性,如果没有会访问这个info字典下面是否存在名为"keys"的键,这就是Django模版语言的字典查找方式;
Django模版标签
    模版标签,主要用于一些复杂的数据处理的方法,如逻辑判断语句、循环语句等,它采用单大括号结合百分号的方式表示,如果{% tag %};
逻辑判断标签
    逻辑判断标签,所谓逻辑判断,就是我们平常使用的IF语句,它一样支持单分支、多分支,同时它也支持很多的判断运算符,如常用的大于、小于、等于等,那么常用的一些判断运算符如下;
操作符
意义
==
等值判断
!=
不等值判断
<
小于判断
>
大于判断
<=
小于等于判断
>=
大于等于判断
in
包含判断
not in
不包含判断
and
or
not
    可以看到这些操作符,其实和我们的Python语言中的一些操作符基本是一摸一样的,大部分也是继承自Python的特点而来的,如下示例;
[cce@doorta ~]# cat /usr/local/Project/blog/user/templates/index.html
{% if "yml" in members %}
    <h1>张三</h1>
{% elif 'cfj' in members %}
    <h1>李四</h1>
{% else %}
    <h1>王五</h1>
{% endif %}%%
[cce@doorta ~]# curl http://127.0.0.1:8000/
    <h1>李四</h1>
  • 提示:其实在Django的模版语言中,提供的逻辑判断语句还有很多,如ifchanged、ifequal、ifnotequal,但是由于模版技术其实后面用得不是太多,所以这些直接看Django的官方文档吧;
循环处理
    对于Django模版语言来讲,它也是一种基础语言,所以在这个语言中,也支持了循环语句,即For语句,用于循环Python内所有可以迭代的对象;
    那么对于For循环来讲,Django模版语言中添加了一些可能会用到的属性,如下;
forloop.counter:当前循环的下标。以1作为起始值;
forloop.counter0:当前循环的下标。以0作为起始值;
forloop.revcounter:当前循环的反向下标值。比如列表有5个元素,那么第一次遍历这个属性是等于5,第二次是4,以此类推。并且是以1作为最后一个元素的下标;
forloop.revcounter0:类似于forloop.revcounter。不同的是最后一个元素的下标是从0开始;
forloop.first:是否是第一次遍历;
forloop.last:是否是最后一次遍历;
forloop.parentloop:如果有多个循环嵌套,那么这个属性代表的是上一级的for循环;
    如下示例,循环迭代出一个列表中的数据,并记录每一次循环的行号;
[cce@doorta ~]# cat /usr/local/Project/blog/user/templates/index.html
{% for x in members %}
    <p>{{ x }}</p>
    <span>{{ forloop.counter0 }}</span>
{% endfor %}%
[cce@doorta ~]# curl http://127.0.0.1:8000/
    <p>cce</p>
    <span>0</span>
    <p>cfj</p>
    <span>1</span>
    <p>dxf</p>
    <span>2</span>
  • 注意:模版语言下面的模版标签其实还有很多,因为后期大多数情况下都是使用前后分离的形式开发,所以在此就不过多赘述,详情请查看官方文档;
Django模版过滤器
    过滤器从字面的意思上,可以理解为,过滤掉不需要的,剩下我们需要的,Django的模板语言同样也内置了过滤器,过滤器作用是在变量输出时,对输出的变量值做进一步的处理,比如,我们可以使用过滤器来更改变量的值;
    过滤器跟模板标签一样,也是在模板中对函数进行调用,比如,对输出的日期进行格式化处理,或者转换大小写字母等,这些都有对应的过滤器去处理它们。当内置过滤器满足不了需求的情况下,也可自定义过滤器。过滤器的语法格式如下;
{{ 变量 | 过滤器1:参数值1 | 过滤器2:参数值2 ... }}
    从语法格式我们可以得知过滤器使用|管道符进行变量与过滤器之间的连接,过滤器的可以通过组合多个过滤器实现链式调用,目前过滤器最多接受一个参数;
常见过滤器
    过滤器相比模板标签要简单的多,我们可以把它们理解成一个Python函数,传递参数给他处理就可以了,当滤器接收参数后对它进行处理,最终将处理结果返回到模板中,这就是整个过滤器的实现流程;
    那么对于Django模版语言来讲,它也提供了很多内置过滤器,如下;
过滤器
使用说明
length
获取变量的长度,适用于字符串和列表
lower/upper
转换字符串为小写/大写形式
first/last
获取变量的首个/末尾元素
add:”n
数值加法,给变量值增加n
safe
默认不对变量内的字符串进行html转义
cut:” “
从给定的字符串中删除指定的值
dictsort:”age”
获取字典列表,并返回按参数中给定键排序的列表
join:”/”
用字符串连接列表,例如 Python 的 str.join(list)
truncatewords:’n
如果字符串字符多于指定的字符数量,那么会被截断。 截断的字符串将以可翻译的省略号序列(“…”)结尾
capfirst
将值的第一个字符大写。如果第一个字符不是字母,则此过滤器无效
default
定义默认值,如果value值为空,则使用给定的默认值;
divisibleby:’n’
判断变量是否可以被n整除,返回布尔值
escape
转义字符串的HTML,此转换器会将<转换为&lt,>转换为&gt等
自定义过滤器
    自定义过滤器也非常简单,也是考虑到后期使用Django模版这种方式去渲染HTML文档的情况很少,同时我们也不推荐在模版语言中去做过多的数据处理,正常情况下,我们应该在Django的视图处理函数层面将数据直接处理好,然后拿着处理好的数据进行模版占位符替换,因此对于自定义过滤器不做过多赘述,详细定义语法,查询官方文档即可;

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注