Django 模板系统

Posted midworld

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django 模板系统相关的知识,希望对你有一定的参考价值。

作为一个 Web 框架,Django 需要一个动态生成 html 的便捷方法。最常用的方法就是模板。模板包含所需 HTML 输出的静态部分以及描述动态内容被插入的一些特殊语法。

Django 项目可以配置一个或多个模板,甚至没有,Django 内置了一套自己的模板系统,叫作 Django template language。

通过模板,我们可以将后端(变量、对象、数据库数据、时间等)渲染到前端 HTML。

模板中常用两种特殊符号

  • 变量名
  • % 逻辑相关 %

1. 模板变量

当模板引擎遇到一个变量时,将计算这个变量,用原有的值替换这个变量。变量命名可以是 字母、数字以及下划线组合,但是不能有空格或标点符号。

语法:

 变量名 

模板变量中的点【.】

在模板中点有特殊含义,它可能是:

  • 字典查询
  • 属性或方法查询
  • 数字索引查询

Tips:
在 Python 中调用一个方法时,往往都会有小括号,而在模板中是不用带括号的。另外需要注意的是模板中调用方法,不能调用带参数的方法。


示例

#views.py
from django.shortcuts import render
def index(request):
    data_list = [1, 2, 3]
    dic = 'name': 'rose'
    tt = datetime.datetime.now()    # 传时间
    return render(request, 'index.html', 'data_list': data_list, 'dic': dic, 'tt': tt)
    # return render(request, 'index.html', locals())    # 用 locals() 一句话即可将所有变量传递

模板中调用:

<html>
    <body>
         data_list.0        <!--调用 data_list 中第一个-->
         dic.name
         tt 
    </body>
</html>

2. 变量过滤器 Filters

过滤器可以改变变量的的显示

语法:

 value|filter_name: 参数      # 管道符 | 用来应用过滤器,管道符两侧无空格

注意

  • 一个过滤器的输出结果可以作为另一个的输入
  • 过滤器可以接受参数
  • 过滤器参数包含空格的话,必须用引号包裹

2.1 Django 内置过滤器

default

若一个变量为 False 或空,可以使用 default 给其指定默认值

 value|default: 32      # 若变量 value 为 False 或空,默认为 32

length

返回变量的长度,变量必须为 list 或 str

# value = [1, 2, 3]
 value|length        # 值为 3

filesizeformat

格式化为人为可读的文件尺寸,如:KB、MB、以及 bytes 等

# value = 123456789
 value|filesizeformat   # 117.7MB

slice

切片操作

 value|slice:"2:-1" 

date

格式化日期时间

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

safe

Django 模板系统中会自动对 HTML 标签以及 JS 语法进行转义,保护系统安全。但有时我们并不希望它被转义,而是保留原有的意思,那么就可以使用 safe 告诉 Django 这个是安全的。

<!-- value = "<span>你好</span> -->
 value|safe     

<!-- 也可以用 autoescape 标签 -->
% autoescape off %   
     a 
% endautoescape %    

truncatewords

在指定数量后截断字符串

<!-- value = "hello" -->
 value|truncatewords:3

cut

移除变量中与参数相同的字符

# value = "hello world"
 value|cut:" "     # helloworld

join

字符串连接列表

# value = 'he'
 v|join:"[1, 2]"    # h[1, 2]e 

timesince

将日期格式设为自该日期起的时间

# create_time 必须是一个日期时间格式,不能是字符串
# create_time = 2019.04.04 
# comment_time = 2019.04.08
 create_time|timesince:comment_time     # 4 天

timeuntil

测量变量到给定日期或时间的时间间隔

# create_time = 2019.04.04 01:00
# comment_time = 2019.04.04 09:00
 create_time|timesince:comment_time   # 间隔 8 h

其他过滤

  • urlencode:编码中文
  • first:第一个
  • upper:变为大写,对应的还有 lower
  • add:增加
  • addslashes 给变量中的引号前加上斜线
  • capfirst 首字母大写

更多过滤:https://docs.djangoproject.com/zh-hans/2.1/ref/templates/builtins/#ref-templates-builtins-filters

2.2 自定义 filter 和 simple_tag

虽然 Django 给我们提供了丰富的内置过滤器,但有时并不能完全满足需求,这就需要我们自定义过滤器了,自定义过滤器分为以下几个步骤:

  • 在 app 中创建一个名为 templatetags 的模块
  • 在模块中创建一个 .py 文件,如:my_tags.py
  • 在 my_tags.py 中自定义过滤器
  • 在 HTML 中调用 % load my_tags %
  1. 在 app 中创建一个 templatetags 模块,该模块中包含一个 my_tags.py 文件:

技术图片

  1. 自定义过滤器,my_tags.py:
from django import template
from django.utils.safestring import mark_safe

# 固定格式
register = template.Library()

# 自定义一个乘法过滤器
@register.filter
def multi(value, arg):
    return value*arg

@register.filter(name="he")
def add_he(value):
    return " SB".format(value)

# simple_tag,name 可以在模板中当做过滤器使用
@register.simple_tag(name='sm')
def simple_multi(value, a, b, c):
    return value*a+b-c
  1. 视图函数 app\views.py:
def test(request):
    num = 28        # 变量 num,使用 locals() 将变量传给模板
    retutn render(request, 'test.html', locals())
  1. 模板中使用过滤器 test.html:
% load my_tags %   <!--导入 my_tags -->

 num | multi:2   <!--28 * 2-->

<!-- 28 * 2 + 3 -4 -->
% sm num 2 3 4 %     

 d.name|he 

总结

  • 自定义 filter 有参数限制,最多两个,其中一个为变量本身。
  • simple_tag 标签,没有参数限制,但不能放在 if、for 语句中。
  • 自定义 filter、simple_tag 标签时,可以给它们定义一个 name,在模板中可以替代它们使用。

3 标签 Tags

标签 Tags 在模板中有特殊含义,如:for 循环、if 条件语句等。

一个完整的 Tags 包括开始和结束,必须以 % endXX % 结束该标签。

3.1 for 循环

语法:

# 这是一个 for 循环语法格式
% for data in data_list %
     data.username 
% endfor %

for 循环常用参数

变量 描述 变量 描述
forloop.counter 当前循环的索引值(从1开始) forloop.counter0 当前循环的索引值(从0开始)
forloop.revcounter 当前循环的倒序索引值(从1开始) forloop.revcounter0 当前循环的倒序索引值(从0开始)
forloop.first 当前循环是不是第一次循环(布尔值) forloop.last 当前循环是不是最后一次循环(布尔值)
forloop.parentloop 本层循环的外层循环
<!-- data_list = ['name': 'rose']  -->
<!-- empty 下的内容将不会显示 -->

% for data in data_list %
     data.name 
% empty %
    <li>ksksksksk </li>
% endfor %

3.2 if 条件语句

if 条件语句包含 elif、else,同样地也支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断等

% if data_list %
     data_list|length 
% else %
     data_list|cut:" " 
% endif %

3.3 with

当变量名比较长的时候,可以用来简写变量名。

<!-- saadfva = 'dddddddddddddddddaaaaaa' -->
<!-- 给 saadfva 去别名 s -->
% with saadfva as s %
     s      
% endwith %

3.4 csrf_token

生成 csrf_token 标签,用于防止跨站攻击验证,常用于 form 表单提交时,解决 403(forbidden)问题。

<form>
    % csrf_token %
</form>

3.5 % url ‘name‘ %

引用路由配置地址,或叫反向查找视图函数,常用于 form 表单中 action 属性值。

<!--index 为视图函数名-->
<form action="% url 'index'%">
</form>

3.6 verbatim

禁止渲染,当想渲染的内容就是 hello 时,可以禁止 render。

% verbatim %
     hello 
% endverbatim %

3.7 static

在页面中加载静态文件

% load static %
<script scr=% static 'js/jquery.3.3.1.js' %

4. 模板继承

在使用 Django 开发中,往往会写很多重复的 HTML 内容,这时我们可以写一个 母版,再通过模板继承的方式来继承该母版,以减少重复代码。

下面是一个母版,我们在母版中定义了 title、header、content、jsblock 块,子页面在继承时,可以通过 block 名来替换相应的母版中的内容。

% load static %
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>% block title %% endblock %</title>
    % block css %% endblock %
</head>
<body>
<div class="page-header"></div>
<div class="page-body">
    <!--主要内容区-->
    <div class="contents">
        % block content %% endblock %
    </div>
</div>
% block js % % endblock %

</body>
</html>

4.1 extend 继承

使用 extend 标签可以用来继承 母版

母版 base.html

% load static %
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>% block title %% endblock %</title>
    % block css %% endblock %
</head>
<body>
<div class="page-header"></div>
<div class="page-body">
    <!--主要内容区-->
    <div class="contents">
        % block content %
            <h1>你好</h1>
        % endblock %
    </div>
</div>
% block js % % endblock %

</body>
</html>

子页面 index.html

<!-- 在子模板中继承基础模板,base.html 为基础模板 -->
% extends 'base.html' %

% block content %
     block.super 
% endblock %

要想在子页面中使用母版中 block 包裹的内容,需要使用 block.super 将其加载过来

4.2 include 组件

可以将常用的页面内容,如:导航条、页脚等组件单独保存在独立的文件中,再通过 include 标签将其加载过来即可。

子页面 index.html

<!-- 在子模板中继承基础模板,base.html 为基础模板 -->
% extends 'base.html' %

% include 'navbar.html' %

4.3 inclusion_tag

多用于返回 HTML 片段。

如:开发一个博客系统,左侧栏的标签、分类、日期归档,在博客文章主页和文章详情页是一样的。为了减少代码重复性,且能提高页面的拓展性。我们就用 inclusion_tag 来实现左侧栏。

  1. 在项目中新建一个名为 templatetags 的包
  2. templatetags 包中定义一个 my_tags.py 文件,编辑如下:
from django import template
from blog import models
from django.db.models import Count

register = template.Library()

# 意思是将返回值返回给模板页 left_menu.html
@register.inclusion_tag('includes/left_menu.html')
def get_left_menu(username):
    """
    侧边栏:分类、标签、日期归档公共部分
    :param user: 用户对象
    :return:
    """
    user = models.UserInfo.objects.filter(username=username).first()
    blog = user.blog

    # 使用 annotate 分组查询所有文章分类
    category_list = models.Category.objects.filter(blog=blog).annotate(c=Count('article')).values('name', 'c')

    # 使用 annotate 分组查询所有文章标签
    tag_list = models.Tags.objects.filter(blog=blog).annotate(c=Count('article')).values('name', 'c')

    # 日期归档,格式为:2019-03
    archive_list = models.Article.objects.filter(user=user).extra(
        select='archive_time': 'date_format(publish_time, "%%Y-%%m")'
    ).values('archive_time').annotate(c=Count('pk')).values('archive_time', 'c')

    return 
        "username": username,
        'category_list': category_list,
        'tag_list': tag_list,
        'archive_list': archive_list
    

上面我们从数据库中取出用户的博客文章分类、标签、日期归档列表,并将其传递给 left_menu.html

  1. 创建一个模板页 left_menu.html
<!--博客文章分类-->
<div class="panel panel-success">
    <div class="panel-heading">文章分类</div>
    <div class="panel-body">
        % for category in category_list %
            <p><a href="/blog/ username /category/ category.name "> category.name ( category.c )</a></p>
        % endfor %
    </div>
</div>

<!--博客文章标签-->
<div class="panel panel-warning">
    <div class="panel-heading">文章标签</div>
    <div class="panel-body">
        % for tag in tag_list %
            <p><a href="/blog/ username /tag/ tag.name "> tag.name ( tag.c )</a></p>
        % endfor %
    </div>
</div>

<!--博客文章日期归档-->
<div class="panel panel-info">
    <div class="panel-heading">日期归档</div>
    <div class="panel-body">
        % for archive in archive_list %
            <p><a href="/blog/ username /archive/ archive.archive_time "> archive.archive_time ( archive.c )</a></p>
        % endfor %
    </div>
</div>
  1. 在需要增加左侧栏的页面添加 inclusion_tag 即可
def article_detail(request):
    username = 'rose'
    return render(request, 'article_detail.html', 'username': username)
<!-- article_detail.html -->

% load my_tags %

% get_left_menu username %

Tips: username 需要传递

?
?
?
?
?
?
?

以上是关于Django 模板系统的主要内容,如果未能解决你的问题,请参考以下文章

Django的模板系统

Django模板系统

Django路由系统视图模板

Django之博客系统:自定义模板标签

Django模板系统

Django之模板系统