模板语法中主要包括变量标签

变量

Django中{{ }}这样的语句就是变量。

比如我想取列表ls的下标为0的元素:

 {{ ls.0 }}

过滤器

过滤器是在变量中进一步操作变量的语法。

基本语法

{{ obj|filter_name:param }}

obj为变量的名字;filter_name为某一个过滤器的名字;param是相应过滤器的参数。

常用的过滤器

date

# 在views.py文件中我们如果不对时间进行处理,比如直接取n值:
# 那可以在模板里进行时间格式的转化
# n=datetime.datetime.now()
{{n|date:'Y-m-d'}}

default

# 如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。例如:
{{value|default:'nothing'}}

length

# 返回值的长度。它对字符串和列表都起作用。例如:
{{value|length}}

slice

# 切片 假如value为一个字符串,找到它下标为2-5切片组成的新字符串
{{value|slice:'2:5'}}

truncatechars

# 如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
# 参数:要截断的字符数 
# 注意结尾的'...'算三个字符,如果参数<=3,那浏览器上显示的永远是'...'
{{value|truncatechars:5}}

safe

# Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。
# 为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。比如:
# views.py文件中的字符串是这样的:
    value="<a href="">点击</a>"
# 那index.html中告诉浏览器正常渲染就行,最终得到的是一个a标签:
    {{value|safe}       

标签

Django中{% %}这样的语句就是标签。

标签比变量更加复杂,一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。

一些标签需要开始和结束标签

{% tag %} 
...标签内容 ...
{% endtag %}

for标签

遍历每一个元素

可以利用{% for obj in list reversed %}反向完成循环。 例如:遍历一个字典

{% for key,value in dic.items %}
    <p>{{key}}:{{value}}</p>
{% endfor %}

循环序号可以通过{{forloop}}显示

##forloop.counter            The current iteration of the loop (1-indexed)
##forloop.counter0           The current iteration of the loop (0-indexed)
##forloop.revcounter         The number of iterations from the end of the loop (1-indexed)
##forloop.revcounter0        The number of iterations from the end of the loop (0-indexed)
##forloop.first              True if this is the first time through the loop
##forloop.last               True if this is the last time through the loop

{% for key,value in dic.items %}
    <p>{{forloop.counter0}}:{{key}}:{{value}}</p>
{% endfor %}

for ... empty

for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,可以有所操作。

{% for person in person_list %}
    <p>{{ person.name }}</p>

{% empty %}
    <p>sorry,no person here</p>
{% endfor %}

if标签

{% if %}会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。

{% if num > 100 or num < 0 %}
    <p>无效</p>
{% elif num > 80 and num < 100 %}
    <p>优秀</p>
{% else %}
    <p>凑活吧</p>
{% endif %}

with标签

使用一个简单地名字缓存一个复杂的变量,当你需要使用一个“昂贵的”方法(比如访问数据库)很多次的时候是非常有用的。

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

csrf_token

这个标签用于跨站请求伪造保护

不注释的情况下我们可以这样做:在login.html文件里加上csrf_token就可以了:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/whw1.css">
</head>
<body>
<form action="http://127.0.0.1:8000/login/" method="post">
    {{# 注意要在form表单里面加 #}}
    {% csrf_token %}
    用户名: <input type="text" name="user"  >
    密码: <input type="password" name="pwd">
    <input type="submit">
</form>

</body>
</html>

爬虫发送post请求简单模拟:

import requests

res = requests.post('http://127.0.0.1:8000/login/',data={
    'username':'whw',
    'password':'123'
})
print(res.text)

'django.middleware.csrf.CsrfViewMiddleware', 全局中间件的配置

通过它还有一种方式:装饰器的形式

from django.views.decorators.csrf import csrf_exempt,csrf_protect
#放行csrf认证,即便是全局配置了csrf认证
@csrf_exempt  
def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    else:
        username = request.POST.get('username')
        print(request.POST) #<QueryDict: {'csrfmiddlewaretoken': ['OhZYiA1NHjh6ywLldzLOzy4N2OXOmHZH4tIlZvTe5ll0p6OSNUhuEgu3aOalUZoo'], 'username': ['chao']}>
        print(username)
        return HttpResponse('ok')

#还有一种强制保护
@csrf_protect  #强制csrf认证,即便是全局没有配置csrf认证
def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    else:
        username = request.POST.get('username')
        print(request.POST) #<QueryDict: {'csrfmiddlewaretoken': ['OhZYiA1NHjh6ywLldzLOzy4N2OXOmHZH4tIlZvTe5ll0p6OSNUhuEgu3aOalUZoo'], 'username': ['chao']}>
        print(username)
        return HttpResponse('ok')

标签与过滤器的一个简单实例

url

re_path('^index/$',views.index),

视图函数

from django.shortcuts import render,HttpResponse
def index(request):
    import datetime
    n = datetime.datetime.now()
    now = strftime('%Y-%m-%d %X')

    #变量
    name = 'wanghwqweqwasdqwe'
    list_w = [111,222,333]
    dic_w = {'w':1,'h':2}

    class Person:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    person_1 = Person('wa1',22) #自定义类对象
    person_2 = Person('wa2',23) #自定义类对象
    person_3 = Person('wa3',24) #自定义类对象
    person_list = [person_1,person_2,person_3]

    return render(request,'index.html',{
                'datetime':now,
                'name':name,
                'ls':list_w,
                'dic':dic_w,
                'person1_name_upper':person_1.name.upper(),
                'person_2_age':person_list[1].age,
                'n':n,
            })

    #这里也可以简写成locals()——字典的key值与变量名一样!——>字典的key值与value值一样!
    #return render(requset,'index.html',locals())

模板文件的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    {#变量#}
    <p>{{ datetime }}</p>
    <p>{{ name }}</p>
    <p>{{ ls }}</p>
    <p>{{ ls.0 }}</p>
    <p>{{ dic }}</p>
    <p>{{ person1_name_upper }}</p>
    <p>{{ person_2_age }}</p>
    <p>{{ ls|length }}</p>
    <p>{{ name|slice:'2:5' }}</p>
    <p>{{ name|truncatechars:7 }}</p>
    <p>{{ n|date:'Y-m-d ' }}</p>

    {#标签#}
    {#遍历字典#}
    {% for key,value in dic.items %}
        <p>{{ forloop.counter0 }}:{{ key }}:{{ value }}</p>
    {% endfor %}
    {#条件判断#}
    {% if ls.0 > 200 %}
        <p>可以可以</p>
    {% elif ls.0 > 100 and ls.0 < 200  %}
        <p>呵呵哒</p>
    {% else %}
        <p>哈哈哈哈哈</p>
    {% endif %}

    {#自定义过滤器#}
    {% load my_tag_filter %}
    <p>{{ ls.0|multi_filter:20}}</p>

     <h4>自定义标签</h4>
    <p>{% simple_tag_multi 5 6 %}</p>
    {#记得给自定义的input传参#}
    <p>{% my_input 'w' 'asd' %}</p>

</body>
</html>

组件

组件其实就是一个完整的模块,将这个完整功能模块放到一个hmtl文件中,使用这个模块的其他页面,直接在页面中引入即可, 引入方式为{% include '模块.html' %},可以在任意位置引入。

组件与插件的区别

组件是提供某一完整功能的模块,如:编辑器组件,QQ空间提供的关注组件 等。 而插件更倾向封闭某一功能方法的函数。 这两者的区别在 Javascript 里区别很小,组件这个名词用得不多,一般统称插件。

使用组件的一个简单实例

这里我有两个页面:1.html与2.html 1.html的内容是这样的

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>哈哈哈哈</title>
</head>

<body>
    <div id="sidebar">
        {# 在页面1中直接引入页面2,此时页面2就是一个组件 #}
        {% include '2.html' %}
    </div>


</body>
</html>

2.html的内容是这样的

<div>
    <a>哈哈哈哈哈</a>
</div>

如果1.html与2.html这两个文件在同一个package中,那么1.html可以直接显示2.html的内容了!

自定义标签及过滤器

当然,如果Django自带的标签及过滤器如果不够用的话我们还可以自定义标签及过滤器。

简单步骤

1、app应用文件夹中创建一个叫做templatetags的文件夹

2、在templatetags里面创建一个py文件,例如:mytag.py

3、mytag.py:

from django import template
from django.utils.safestring import mark_safe #mark_safe让字符串形式的标签生效 后面有实例

register = template.Library()   #register的名字是固定的,不可改变


# 自定义乘法过滤器
@register.filter
def filter_multi(v1,v2):
    return  v1 * v2

# 自定义乘法标签
@register.simple_tag  #和自定义filter类似,只不过接收更灵活的参数,没有个数限制。
def simple_tag_multi(v1,v2):
    return  v1 * v2

@register.simple_tag
def my_input(id,arg):
    result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
    # 返回字典格式的数据 
    # mark_safe让字符串形式的标签生效
    return {'ret':mark_safe(result)})

4、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py

{% load my_tags %} 

5、使用simple_tag和filter(如何调用)

-------------------------------.html
{% load my_tags %}  

# num=12
{{ num|filter_multi:2 }} #24

{{ num|filter_multi:"[22,333,4444]" }}

{% simple_tag_multi 2 5 %}  参数不限,但不能放在if for语句中
{% simple_tag_multi num 5 %}

{#记得给自定义的input传参#}
<p>{% my_input 'w' 'asd' %}</p>

6、自定义的乘法过滤器与自定义乘法标签的区别

(1)过滤器最多只能定义两个形参,这是由它的调用形式决定的,因为过滤器调用的时候无法穿第三个参数(如上面例子,只能调用ls.0与3)而multi_tag自定义标签可以有多个形参 。

(2)但是如果进行“逻辑判断”的时候,就得用自定义过滤器了,因为filter可以用在if等语句后,simple_tag不可以,如:

{% if num|filter_multi:30 > 100 %}
    <p>{{ num|filter_multi:30 }}</p>
{% endif %}

inclusion_tag

Inclusion_tag本质上也是一个自定义的标签,多用于返回html代码片段

举例说明

templatetags/my_inclusion.py文件中:

from django import template

register = template.Library()
 #将result.html里面的内容用下面函数的返回值渲染,然后作为一个组件一样,加载到使用这个函数的html文件里面
@register.inclusion_tag('result.html') 
 #参数可以传多个进来
def show_results(n):
    n = 1 if n < 1 else int(n)
    data = ["第{}项".format(i) for i in range(1, n+1)]
    return {"data": data}#这里可以穿多个值,和render的感觉是一样的{'data1':data1,'data2':data2....}

templates/snippets/result.html文件作为片段的内容:

<ul>
  {% for choice in data %}
    <li>{{ choice }}</li>
  {% endfor %}
</ul>

templates/index.html文件引入上面的片段

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>inclusion_tag test</title>
</head>
<body>

{% load my_inclusion %}

{% show_results 10 %} 
 <!-- 这个10 是参数 -->
</body>
</html>

inclusion_tag参考博客

Django模版语言inclusion_tag的用法

模板的继承

Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可以让您创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的blocks。

模版继承的步骤

1、创建一个模板.html文件。

2、需要继承的子文件中必须在“开头“位置引入模板文件{% extends '模板.html' %}

3、模板.html 中将需要被子文件修改的部分指出来:{% block content %} xxxxxxx{% endblock %},还可以指定名字结束{% endblock content%}

4.1、继承模板的子文件里面开始写自己的内容:{% block content %} 自己html里面的内容{% endblock %}

4.2、保留模板内容的写法{% block content %}{{ block.super }}自己html里面的内容{% endblock %} ,如果不加{{ block.super }}的话,模板中写的内容在子页中是无法显示的,加上的话就显示出来了,而且加的位置不一样显示的位置也不一样!

使用模版继承的一些提示

(1)如果你在模版中使用 {% extends %} 标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作。
(2)在base模版中设置越多的 {% block %} 标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,
 你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。
(3)如果你发现你自己在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个 {% block %} 中。
(4)为了更好的可读性,你也可以给你的{% endblock %} 标签一个 名字 。例如:
    {% block content %}
    ...
    {% endblock content %}
在大型模版中,这个方法帮你清楚的看到哪一个  {% block %} 标签被关闭了。  
(5)不能在一个模版中定义多个相同名字的 block 标签。 
(6)If you need to get the content of the block from the parent template, the variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template.

模板继承的一个实例

我们先创建一个base的路由与视图:

# 路由
re_path('^base/$',views.base),
# 视图
def base(request):
    return render(request,'base.html')  

在templates模板目录中加入base.html母版文件:

<!DOCTYPE html>
<html lang="en">
    <head>
        <link rel="stylesheet" href="style.css" />
        <title>{% block title %}My amazing site{% endblock %}</title>
    </head>

    <body>
        <div id="sidebar">
            {% block sidebar %}
            <ul>
                <li><a href="/">Home</a></li>
                <li><a href="/blog/">Blog</a></li>
            </ul>
            {% endblock %}
        </div>

        <div id="content">      
            {% block content %}{% endblock %}
        </div>
    </body>
</html>

母版说明:

这个模版,我们把它叫作母版,它定义了一个可以用于两列排版页面的简单HTML骨架。
“子模版”的工作是用它们的内容填充空的blocks。
在这个例子中,block标签定义了三个可以被子模版内容填充的block。
block 告诉模版引擎:子模版可能会覆盖掉模版中的这些位置。

子模板我们命名为child1,他的路由与视图的逻辑如下:

# 路由
re_path('^child1$',views.child1),
# 视图
def child1(request):
    list_w = [111,222,333]
    return render(request,'child1.html',{'list_w':list_w})

子模板child1.html中的内容为:

{% extends 'base.html' %}

{% block title %} 哈哈哈哈 {% endblock %}
{% block content %}
    <p>{{ list_w }}</p>
{% endblock %}

子模版说明1:

extends标签是这里的关键。它告诉模版引擎,这个模版“继承”了另一个模版。当模版系统处理这个模版时:
首先,它将定位父模版——在此例中,就是“base.html”。
然后,模版引擎将注意到 base.html 中的三个 block标签,并用子模版中的内容来替换这些block。
这种方式使代码得到最大程度的复用,并且使得添加内容到共享的内容区域更加简单,例如,部分范围内的导航。
请注意,子模版并没有定义 sidebar block,所以系统使用了父模版中的值。
被继承的母版的 {% block %} 标签中的内容总是被用作备选内容(fallback)。

子模版说明2:根据 child1.html 的值,输出可能看起来是这样的

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>哈哈哈哈</title>
</head>

<body>
    <div id="sidebar">
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
    </div>

    <div id="content">
        <p> [111,222,333]</p>
    </div>
</body>
</html>