作为一个 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 %}
- 在 app 中创建一个 templatetags 模块,该模块中包含一个 my_tags.py 文件:
- 自定义过滤器,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
- 视图函数 app\views.py:
def test(request): num = 28 # 变量 num,使用 locals() 将变量传给模板 retutn render(request, 'test.html', locals())
- 模板中使用过滤器 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、js
等 block 块
,子页面在继承时,可以通过 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
来实现左侧栏。
- 在项目中新建一个名为
templatetags
的包 - 在
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
。
- 创建一个模板页
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>
- 在需要增加左侧栏的页面添加
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 需要传递
来源:https://www.cnblogs.com/midworld/p/11380497.html