July's Blog

花有重开日,人无再少年

0%

django笔记03 - 模板

模板

模板放在根目录的templates文件夹下,如果找不到可能要修改settings.py文件,具体的之后再说

渲染模板

  1. render_to_string

将模板编译后,渲染成python的字符串格式,最后通过HttpResponse类包装成一个HttpResponse对象返回

1
2
3
4
5
6
from django.template.loader import render_to_string
from django.http import HttpResponse

def index(request):
html = render_to_string('index.html')
return HttpResponse(html)
  1. render更简单

将模板渲染成字符串和包装成HttpResponse对象一部到位完成

1
2
3
4
from django.shortcuts import render

def index(request):
return render(request, 'index.html')

模板查找路径

settings.py文件的templates部分进行配置

  1. DIRS列表:,在这个列表中可以存放所有的模板路径,以后在视图中使用render或者render_to_string渲染模板的时候,会在这个列表的路径中查找模板

  2. APP_DIRS:默认为True,这个设置为True后,会在INSTALLED_APPS(已安装的APP)下的templates文件夹中查找模板,文件夹名必须为templates

  3. 查找顺序:优先级为,DIRS-> 当前APP的templates文件夹-> 其它INSTALLED_APPStemplates文件夹。比如代码render(‘list.html’)。先会在DIRS这个列表中依次查找路径下有没有这个模板,如果有,就返回。如果DIRS列表中所有的路径都没有找到,那么会先检查当前这个视图所处的app是否已经安装,如果已经安装了,那么就先在当前这个app下的templates文件夹中查找模板,如果没有找到,那么会在其他已经安装了的app中查找。如果所有路径下都没有找到,那么会抛出一个TemplateDoesNotExist的异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, "templates")],
'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',
],
},
},
]

模板变量

  1. 视图中,以字典的形式传入模板
1
return render(request, 'index.html', context=context) #context是字典
  1. 在模板中使用变量,以{{ 变量 }}的形式
  2. 访问对象的属性,通过对象.属性名来访问,如访问person对象的username属性,通过person.username
  3. 访问字典的key对应的value,通过字典.key的方式访问,不能通过中括号[]的形式访问,不能使用字典本身的属性名作为key,防止自身属性被忽略,无法访问
  4. 列表和元组,同样使用点.的方式访问,不能通过中括号[]{{ person.0 }}

模板标签

if标签

  1. 所有的标签都是在{% %}之间
  2. if标签有闭合标签,{% endif %}
  3. if标签的判断运算符,与python相同,==、!=、<、<=、>、>=、in、not in、is、is not
  4. 还可以使用elif,else等标签

for...in...标签

类似于Python中的for...in...。可以遍历列表、元组、字符串、字典等一切可以遍历的对象。
没有continuebreak语句

1
2
3
{% for person in persons %}
<p>{{ person.name }}</p>
{% endfor %}

如果想要反向遍历,那么在遍历的时候就加上一个reversed

1
2
3
{% for person in persons reversed %}
<p>{{ person.name }}</p>
{% endfor %}

遍历字典的时候,需要使用itemskeysvalues等方法。在DTL中,执行一个方法不能使用圆括号的形式

1
2
3
4
{% for key,value in person.items %}
<p>key:{{ key }}</p>
<p>value:{{ value }}</p>
{% endfor %}

for循环中,DTL提供了一些变量可供使用。这些变量如下:

  • forloop.counter:当前循环的下标。以1作为起始值。
  • forloop.counter0:当前循环的下标。以0作为起始值。
  • forloop.revcounter:当前循环的反向下标值。比如列表有5个元素,那么第一次遍历这个属性是等于5,第二次是4,以此类推。并且是以1作为最后一个元素的下标。
  • forloop.revcounter0:类似于forloop.revcounter。不同的是最后一个元素的下标是从0开始。
  • forloop.first:是否是第一次遍历。
  • forloop.last:是否是最后一次遍历。
  • forloop.parentloop:如果有多个循环嵌套,那么这个属性代表的是上一级的for循环。

for…in…empty标签

这个标签使用跟for...in...是一样的,只不过是在遍历的对象如果没有元素的情况下,会执行empty中的内容

1
2
3
4
5
{% for person in persons %}
<li>{{ person }}</li>
{% empty %}
暂时还没有任何人
{% endfor %}

with标签

  1. 在模板中,通过with语句来定义变量
  2. with语句有两种使用方式,一种是with xx=xxx的形式(=左右没有空格),第二种是with xxx as xx的形式
  3. 定义的变量只能在with语句块中使用,在with语句块外面取不到这个变量
1
2
3
4
5
{% with person.0  as zs %}
<p>{{ zs }}</p>
<p>{{ zs }}</p>
<p>{{ zs }}</p>
{% endwith %}

url标签

在模版中,我们经常要写一些url,比如某个a标签中需要定义href属性。通过硬编码的方式直接将这个url写死的话,对于以后项目维护不方便。因此建议使用这种反转的方式来实现,类似于django中的reverse一样

1
<a href="{% url 'book:list' %}">图书列表页面</a>  #book可能是app名,或命名空间的名,有待验证。一般直接写name就好

如果url反转的时候需要传递参数,那么可以在后面传递。但是参数分位置参数和关键字参数。位置参数和关键字参数不能同时使用

1
2
3
4
5
6
7
8
# path部分
path('detail/<book_id>/',views.book_detail,name='detail')

# url反转,使用位置参数
<a href="{% url 'book:detail' 1 %}">图书详情页面</a>

# url反转,使用关键字参数
<a href="{% url 'book:detail' book_id=1 %}">图书详情页面</a>

如果想要在使用url标签反转的时候要传递查询字符串的参数,那么必须要手动在在后面添加

1
<a href="{% url 'book:detail' book_id=1 %}?page=1">图书详情页面</a>

如果需要传递多个参数,那么通过空格的方式进行分隔

1
<a href="{% url 'book:detail' book_id=1 page=2 %}">图书详情页面</a>

autosecape标签

  1. 模板中默认开启自动转义。自动转义是可以将一些特殊的字符,转义成html语法能识别的字符,比如<会被转义成&lt,而>会被自动转义成&gt
  2. 如果变量确实是可信任的,可以使用autoescape标签来关掉自动转义
1
2
3
{% autoescape off %}
{% info %}
{% endautoescape %}

verbatim标签:

默认在DTL模板中是会去解析那些特殊字符的。比如{%%}以及`{{`等。如果你在某个代码片段中不想使用DTL的解析引擎。那么你可以把这个代码片段放在`verbatim`标签中

1
2
3
{% verbatim %}
{{if dying}}Still alive.{{/if}}
{% endverbatim %}
## 模板过滤器 在模版中,有时候需要对一些数据进行处理以后才能使用。一般在Python中我们是通过函数的形式来完成的。DTL中,不支持函数的调用形式'()',需要通过过滤器来实现的,而过滤器其实就是一个函数,可以对需要的参数进行处理,并且还可以额外接受一个参数(即最多有两个参数)。过滤器使用的是`|`来使用。 ### add过滤器 将传进来的参数添加到原来的值上面。这个过滤器会尝试将值和参数转换成整形然后进行相加。如果转换成整形过程中失败了,那么会将值和参数进行拼接。如果是字符串,那么会拼接成字符串,如果是列表,那么会拼接成一个列表。
1
{{ value|add:"2" }}
如果`value`是等于4,那么结果将是6。如果`value`是等于一个普通的字符串,比如`abc`,那么结果将是`abc2`。`add`过滤器的源代码如下:
1
2
3
4
5
6
7
8
9
def add(value, arg):
"""Add the arg to the value."""
try:
return int(value) + int(arg)
except (ValueError, TypeError):
try:
return value + arg
except Exception:
return ''
### cut过滤器 移除值中所有指定的字符串。类似于`python`中的`replace(args,"")`
1
{{ value|cut:" " }}
以上示例将会移除value中所有的空格字符。cut过滤器的源代码如下:
1
2
3
4
5
6
7
def cut(value, arg):
"""Remove all values of arg from the given string."""
safe = isinstance(value, SafeData)
value = value.replace(arg, '')
if safe and arg != ';':
return mark_safe(value)
return value
### date过滤器 将一个日期按照指定的格式,格式化成字符串
1
2
3
4
5
6
7
# 数据
context = {
"birthday": datetime.now()
}

# 模版
{{ birthday|date:"Y/m/d" }}
那么将会输出2018/02/01。其中`Y`代表的是四位数字的年份,`m`代表的是两位数字的月份,`d`代表的是两位数字的日 | 格式字符 | 描述 | 示例 | | :------: | :------: | :------: | | Y | 四位数字的年份 | 2018 | | m | 两位数字的月份 | 01-12 | | n | 月份,1-9前面没有0前缀 | 1-12 | | d | 两位数字的天 | 01-31 | | j | 天,但是1-9前面没有0前缀 | 1-31 | | g | 小时,12小时格式的,1-9前面没有0前缀 | 1-12 | | h | 小时,12小时格式的,1-9前面有0前缀 | 01-12 | | G | 小时,24小时格式的,1-9前面没有0前缀 | 1-23 | | H | 小时,24小时格式的,1-9前面有0前缀 | 01-23 | | i | 分钟,1-9前面有0前缀 | 00-59 | | s | 秒,1-9前面有0前缀 | 00-59 | ### default过滤器 如果值被评估为False。比如`[]`,`""`,`None`,`{}`等这些在`if`判断中为`False`的值,都会使用`default`过滤器提供的默认值。示例代码如下:
1
{{ value|default:"nothing" }}
如果`value`是等于一个空的字符串。比如`""`,那么以上代码将会输出`nothing` ### default_if_none 如果值是`None`,那么将会使用`default_if_none`提供的默认值。这个和`default`有区别,`default`是所有被评估为`False`的都会使用默认值。而`default_if_none`则只有这个值是等于`None`的时候才会使用默认值。示例代码如下:
1
{{ value|default_if_none:"nothing" }}
如果`value`是等于`""`即空字符串,那么以上会输出空字符串。如果`value`是一个`None`值,以上代码才会输出`nothing` ### first 返回列表/元组/字符串中的第一个元素
1
{{ value|first }}
如果`value`是等于`['a','b','c']`,那么输出将会是`a` ### last 返回列表/元组/字符串中的最后一个元素
1
{{ value|last }}
如果`value`是等于`['a','b','c']`,那么输出将会是`c` ### floatformat 使用四舍五入的方式格式化一个浮点类型。如果这个过滤器没有传递任何参数。那么只会在小数点后保留一个小数,如果小数后面全是0,那么只会保留整数。当然也可以传递一个参数,标识具体要保留几个小数。 1. 没有传递参数 | value | 模版代码 | 输出 | | --- | --- | --- | | 34.23234 | {{ value\|floatformat }} | 34.2 |
| 34.000 | {{ value\|floatformat }} | 34 |
| 34.260 | {{ value\|floatformat }} | 34.3 |

  1. 传递参数
value 模版代码 输出
34.23234 {{value\|floatformat:3}} 34.232
34.0000 {{value\|floatformat:3}} 34.000
34.26000 {{value\|floatformat:3}} 34.260

join

类似与Python中的join,将列表/元组/字符串用指定的字符进行拼接。示例代码如下:

1
{{ value|join:"/" }}

如果value是等于['a','b','c'],那么以上代码将输出a/b/c

length

获取一个列表/元组/字符串/字典的长度。示例代码如下:

1
{{ value|length }}

如果value是等于['a','b','c'],那么以上代码将输出3。如果valueNone,那么以上将返回0

lower

将值中所有的字符全部转换成小写。示例代码如下:

1
{{ value|lower }}

如果value是等于Hello World。那么以上代码将输出hello world。

upper

类似于lower,只不过是将指定的字符串全部转换成大写。

random

在被给的列表/字符串/元组中随机的选择一个值。示例代码如下:

1
{{ value|random }}

如果value是等于['a','b','c'],那么以上代码会在列表中随机选择一个。

safe

标记一个字符串是安全的。也即会关掉这个字符串的自动转义。示例代码如下:

1
{{value|safe}}

如果value是一个不包含任何特殊字符的字符串,比如<a>这种,那么以上代码就会把字符串正常的输入。如果value是一串html代码,那么以上代码将会把这个html代码渲染到浏览器中。

slice

类似于Python中的切片操作。示例代码如下:

1
{{ some_list|slice:"2:" }}

以上代码将会给some_list2开始做切片操作。

stringtags

删除字符串中所有的html标签。示例代码如下:

1
{{ value|striptags }}

如果value<strong>hello world</strong>,那么以上代码将会输出hello world

truncatechars

如果给定的字符串长度超过了过滤器指定的长度。那么就会进行切割,并且会拼接三个点来作为省略号。示例代码如下:

1
{{ value|truncatechars:5 }}

如果value是等于北京欢迎您~,那么输出的结果是北京...。可能你会想,为什么不会北京欢迎您...呢。因为三个点也占了三个字符,所以北京+三个点的字符长度就是5。

truncatechars_html

类似于truncatechars,只不过是不会切割html标签。示例代码如下:

1
{{ value|truncatechars_html:5 }}

如果value是等于<p>北京欢迎您~</p>,那么输出将是<p>北京...</p>

自定义过滤器

  1. 首先在某个app中,创建一个python包,名为templatetags,必须是这个名,不然找不到。
  2. templatetags包下面,创建一个python文件来存储过滤器
  3. 自定义的过滤器(也就是函数),这个函数的第一个参数永远是被过滤的那个值,还可以再定义另一个参数,过滤器最多只有2个参数
  4. 写完过滤器(函数)后,要使用django.template.Library.filter进行注册
  5. 将过滤器所在的app添加到settings.pyINSTALLED_APPS列表中,不然Django也找不到这个过滤器
  6. 在模板中使用load标签加载过滤器所在的python文件,就可以用了
  7. django.template.Library.filter还可以当做装饰器来使用,如果filter函数没有传递参数,默认使用函数名字作为过滤器名字,也可以传递name参数作为过滤器名字
1
2
3
4
5
6
7
8
9
10
# 某app的templatetags文件夹下的my_fliter.py文件
from django import template

register = template.Library()

@register.filter('my_greet')
def greet(value, word):
return value + word

#register.filter('greet', greet)
1
2
3
4
5
6
<!-- 模板文件 -->
{% load my_fliter %}

<body>
{{ value|greet:' 你好' }}
</body>

模板结构优化

include标签

  1. 重复使用的模板代码,可以单独抽取出来,用到的地方使用include调用
  2. include的模板,可以直接使用当前模板传递进来的变量。也可以使用with xx=xxx传递参数
1
{% include 'header.html' with username='John'}

模板继承

在前端页面开发中。有些代码是需要重复使用的。这种情况可以使用include标签来实现。也可以使用另外一个比较强大的方式来实现,那就是模版继承。模版继承类似于Python中的类,在父类中可以先定义好一些变量和方法,然后在子类中实现。模版继承也可以在父模版中先定义好一些子模版需要用到的代码,然后子模版直接继承就可以了。并且因为子模版肯定有自己的不同代码,因此可以在父模版中定义一个block接口,然后子模版再去实现。以下是父模版的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="{% static 'style.css' %}" />
<title>{% block title %}我的站点{% endblock %}</title>
</head>

<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">首页</a></li>
<li><a href="/blog/">博客</a></li>
</ul>
{% endblock %}
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>

这个模版,我们取名叫做base.html,定义好一个简单的html骨架,然后定义好两个block接口,让子模版来根据具体需求来实现。子模板然后通过extends标签来实现,示例代码如下:

1
2
3
4
5
6
7
8
9
10
{% extends "base.html" %}

{% block title %}博客列表{% endblock %}

{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

注意

  • extends标签必须放在模版的第一行。
  • 子模板中的代码必须放在block中,否则将不会被渲染。

block默认会覆盖父模板的内容,如果在某个block中需要使用父模版的内容,那么可以使用{{block.super}}来继承。比如上例,{%block title%},如果想要使用父模版的title,那么可以在子模版的title block中使用{{ block.super }}来实现。

在定义block的时候,除了在block开始的地方定义这个block的名字,还可以在block结束的时候定义名字。比如{% block title %}{% endblock title %}。这在大型模版中显得尤其有用,能让你快速的看到block包含在哪里。

加载静态文件

在一个网页中,不仅仅只有一个html骨架,还需要css样式文件,js执行文件以及一些图片等。因此在DTL中加载静态文件是一个必须要解决的问题。在DTL中,使用static标签来加载静态文件。要使用static标签,首先需要{% load static %}。加载静态文件的步骤如下:

  1. 首先确保django.contrib.staticfiles已经添加到settings.INSTALLED_APPS中。(默认有)
  2. 确保在settings.py中设置了STATIC_URL。(默认有)
  3. 在已经安装了的app下创建一个文件夹叫做static,然后再在这个static文件夹下创建一个当前app的名字的文件夹,再把静态文件放到这个文件夹下。例如你的app叫做book,有一个静态文件叫做zhiliao.jpg,那么路径为book/static/book/zhiliao.jpg。创建app同名文件夹的目的是,避免多个app的静态文件名相同,调用时发生混淆。
  4. 如果有一些静态文件是不和任何app挂钩的。那么可以在settings.py中添加STATICFILES_DIRS,以后DTL就会在这个列表的路径中查找静态文件。比如可以设置为
1
2
3
4
#根目录下,与templates同级
STATICFILES_DIRS = [
os.path.join(BASE_DIR,"static")
]
  1. 在模版中使用load标签加载static标签。比如要加载在项目的static文件夹下的style.css的文件。那么示例代码如下:
1
2
{% load static %}
<link rel="stylesheet" href="{% static 'style.css' %}">
  1. 如果不想每次在模版中加载静态文件都使用load加载static标签,那么可以在settings.py中的TEMPLATES/OPTIONS添加'builtins':['django.templatetags.static'],这样以后在模版中就可以直接使用static标签,而不用手动的load了。

  2. 如果没有在settings.INSTALLED_APPS中添加django.contrib.staticfiles。那么我们就需要手动的将请求静态文件的url与静态文件的路径进行映射了。很少用到。示例代码如下:

1
2
3
4
5
6
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
# 其他的url映射
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
请作者喝冰阔落