django 模版语法与使用
django模板语言介绍 (摘自官方文档) 链接
- """
- Django模板语言
- Django的模板语言旨在在功能和易用性之间取得平衡。它让那些习惯使用HTML的人感到舒服。
- 如果您对其他基于文本的模板语言(如Smarty 或Jinja2)有过接触,那么您应该对Django的模板感到宾至如归。
- 哲学
- 如果您有编程背景,或者习惯于将编程代码直接混合到HTML中的语言,那么您需要记住,
- Django模板系统不仅仅是嵌入到HTML中的Python。这是一种设计,模板系统用于表达,而不是程序逻辑。
- Django模板系统提供的标签功能类似于一些编程结构 如 if,布尔,for标签,循环标签等 - 但这些标签不是简单地作为相应的Python代码执行,
- 模板系统不会执行任意Python表达式。默认情况下,仅支持下面列出的标记,过滤器和语法(您可以根据需要将自己的扩展添加到模板语言中)。
- 哲学
- 为什么使用基于文本的模板而不是基于XML的模板(如Zope的TAL)?我们希望Django的模板语言不仅可用于XML / HTML模板。
- 在World Online,我们将其用于电子邮件,JavaScript和CSV。您可以将模板语言用于任何基于文本的格式。
- 哦,还有一件事:让人类编辑XML是虐待狂!
- """
什么是模板?
- 模板只是一个文本文件。它可以生成任何基于文本的格式(HTML,XML,CSV等)。
- 模板包含变量,这些变量在评估模板时将替换为值,而变量则包含控制模板逻辑的标记。
- 只要是在html里面有模板语法就不是html文件了,这样的文件就叫做模板。
模板语句的 注释
- 模板语句的注释
- {#{{ '10'|add_str:'5 '}}#}  
- {#注释内容#}    
变量 {{ 变量 }}
- #views 文件函数
- def template_test(request):
-     name = '钢蛋'
-     age = 18
-     hobby = ['唱', '跳', 'rap', '篮球']
-     lis = []
-     st = ''
-     dic = {
-         'name': '铁蛋',
-         'age': 16,
-         'hobby': hobby,
-         'keys': 'xxxxx'
-     }
-     dic_2 = {}
-     return render(request,'template_test.html',
-  {'name':name_p,'age':age,'hobby':hobby,'lis':lis,'st':st,'dic':dic,'dic_2':dic_2})
- # 模板文件html页面
- <!DOCTYPE html>
- <html lang="en">
- <head>
-     <meta charset="UTF-8">
-     <title>郭楷丰</title>
- </head>
- <body>
- {{ 啦啦啦啦啦 }}
- <p>
-     {{ name }}
- </p>
- <p>
-    {{ age }}
- </p>
- <p>
-     {{ hobby }}
- </p>
- <p>
-     {{ lis }}
- </p>
- <p>
-     {{ st }}
- </p>
- <p>
-     {{ dic }}
- </p>
- <p>
-     {{ dic_2 }}
- </p>
- </body>
- </html>

- 小结: {{ }}里填要渲染的变量,规范写法,两边用括号隔开 如 {{ 变量 }}, 后端没有传参的页面不显示
  

点(.)在模板语言中有特殊的含义,用来获取对象的相应属性值。
- #views 文件函数
- def template_test(request):
-     lis = [1, 2, 3,4,5]
-     dic = {"name": "黑蛋"}
-     class Person(object):
-         def __init__(self, name, age):
-             self.name = name
-             self.age = age
-         def dream(self):
-             return " 我 is {} age {}岁...".format(self.name,self.age)
-     gangdan = Person(name="钢蛋", age=18)
-     goudan = Person(name="狗蛋", age=17)
-     tiedan  = Person(name="铁蛋", age=16)
-     person_list = [gangdan, goudan, tiedan]
-     return render(request, "template_test.html", {"lis": lis, "dic": dic, "person_list": person_list})
- # 模板文件html页面
- <!DOCTYPE html>
- <html lang="en">
- <head>
-     <meta charset="UTF-8">
-     <title>郭楷丰</title>
- </head>
- <boby>
-     <p>{{ lis.0 }}</p>   <!--取l中的第一个参数-->
-     <p>{{ dic.name }}</p><!--取字典中key的值-->
-     <p>{{ person_list.0.name }}</p><!--取对象的name属性-->
-     <p>{{ person_list.0.dream }}</p><!--.操作只能调用不带参数的方法-->
- </boby>
- </html>
- #注:当模板系统遇到一个(.)时,会按照如下的顺序去查询:
- 1. 在字典中查询
- 2. 属性或者方法
- 3. 数字索引   # 索引不能为负数


- {% for foo in lis %}
- <!--for循环-->
-     {% if  %}
-     <!--if判断-->
-     {% elif %}
-     <!--elif判断-->
-     {% endif %}
-     <!--if闭合符-->
-     {% else %}
-     <!--else判断不成立执行-->
- {% endfor %}
- <!--for循环闭合符-->
- <!--应用 模板html代码-->
- <form action="" method="post">
-     <label for="inputEmail3" class="col-sm-2 control-label">书名: </label>
-     <div class="col-sm-8">
-         <input type="text" name="book_name" class="form-control" value="{{ edit_obj.title }}">
-         <select name="pub_id" id="" class="btn btn-default  btn-sm">
-             {% for foo in publishers %}
-             {% if foo == edit_obj.pub %}
-             <option selected value="{{ foo.pk }}"> {{ foo.name }}</option>
-             {% else %}
-             <option value="{{ foo.pk }}"> {{ foo.name }}</option>
-             {% endif %}
-             {% endfor %}
-         </select>
-     </div>
-     <div class="text-center text-danger">{{ error }}</div>
-     <div class="col-sm-offset-2 col-sm-10">
-         <button type="submit" class="btn btn-default">提交</button>
-     </div>
- </form>
- <!--应用 python逻辑代码 #views 文件函数-->
- def edit_book(request):
-     error = ''
-     pk = request.GET.get('pk')
-     edit_obj = models.Book.objects.filter(id=pk)
-     if not edit_obj:
-         return HttpResponse('要编辑的数据不存在')
-     if request.method == 'POST':
-         book_name = request.POST.get('book_name')
-         if not book_name.strip():
-             error = "书名不能为空"
-         pub_id = request.POST.get('pub_id')
-         if edit_obj[0].title == book_name and edit_obj[0].pub_id == int(pub_id):
-             error = "未作修改"
-         if not error:
-             obj = edit_obj[0]
-             obj.title = book_name
-             obj.pub_id = int(pub_id)
-             obj.save()
-             return redirect('/book_list/')
-     publishers = models.Publisher.objects.all()
-     return render(request,'edit_book.html',{'edit_obj':edit_obj[0],'publishers':publishers,'error':error})


- if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。
- js,python,模板语言的判断逻辑 - #python
- 10>5>1  =》   10>5  and 5>1   true 
- #js
- 10>5>1  =》   10>5  =》 true   =》   1>1  false
- #模板中  
- 不支持连续连续判断,也不支持算数运算(过滤器)
 
 
- 注意事项
- Django的模板语言不支持连续判断,也不支持以下写法: 
- {% if a > b > c %}
- ...
- {% endif %}
- #不支持算数运算 + - * /
- def xx(request):
-     d = {"a": 1, "b": 2, "c": 3, "items": "100"}
-     return render(request, "xx.html", {"data": d})
- 如上,我们在使用render方法渲染一个页面的时候,传的字典d有一个key是items并且还有默认的 d.items() 方法,此时在模板语言中:
- {{ data.items }}   默认会取d的items key的值。
for循环可用的一些参数:
| forloop.counter | 当前循环的索引值(从1开始) | 
| forloop.counter0 | 当前循环的索引值(从0开始) | 
| forloop.revcounter | 当前循环的倒序索引值(到1结束) | 
| forloop.revcounter0 | 当前循环的倒序索引值(到0结束) | 
| forloop.first | 当前循环是不是第一次循环(布尔值) | 
| forloop.last | 当前循环是不是最后一次循环(布尔值) | 
| forloop.parentloop | 本层循环的外层循环 | 
Filters 过滤器

with 的使用
- 定义一个中间变量(起个别名,只在with内部生效)
- #写法一
- {% with total=business.employees.count %}
-     {{ total }} employee{{ total|pluralize }}
- {% endwith %}
- #写法二
- {% with business.employees.count as total %}
-     {{ total }} employee{{ total|pluralize }}
- {% endwith %}

default 系统默认值
- {{ value|default:"nothing"}}
- 如果value值没传的话就显示nothing
- 注:TEMPLATES的OPTIONS可以增加一个选项:string_if_invalid:'找不到',可以替代default的的作用。
- (在配置文件settings.py设置)


- 将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)例如:
- {{ value|filesizeformat }}
- 如果 value 是 123456789,输出将会是 117.7 MB
add "加"
- 给变量加参数,字符串默认尝试转int类型,转不了就拼接
- {{ value|add:"2" }}
- value是数字4,则输出结果为6
- {{ value|add:"hello" }}
- value是数字666,则输出结果为666hello
- {{ first|add:second }}
- 如果first是 [1,.2,3] ,second是 [4,5,6] ,那输出结果是 [1,2,3,4,5,6] 
lower 小写
- {{ value|lower }}
upper 大写
- {{ value|upper}}
title 标题
- {{ value|title }}
ljust 左对齐
- "{{ value|ljust:"10" }}"
rjust 右对齐
- "{{ value|rjust:"10" }}"
center 居中
- "{{ value|center:"15" }}"
length 长度
- {{ value|length }}
- 返回value的长度,如 value=['a', 'b', 'c', 'd']的话,就显示4.
slice 切片
- {{value|slice:"2:-1"}}
first 取第一个元素
- {{ value|first }}
last 取最后一个元素 
- {{ value|last }}
join 使用字符串拼接列表
- {{ value|join:" // " }}
truncatechars 按照字符截断 ...也计数
- 如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾
- #参数:截断的字符数
- {{ value|truncatechars:9}}
- truncatewords 按照单词进行截断, 只针对英文
date 日期格式化
- #后端
- import datetime
- def mul(request):
-     value = datetime.datetime.now()
-     return render(request, 'mul.html',{'value':value})
- #模板语句
- {{ value|date:"Y-m-d H:i:s"}}   #显示格式 年-月-日 时:分:秒
- #后端
- import datetime
- def mul(request):
-     now = datetime.datetime.now()
-     return render(request, 'mul.html',{'now':now})
- #模板直接使用变量
- {{ now }}  #显示格式 June 19,2019,22:00 p.m.


- 可格式化输出的字符: 点击查看
- 改配置文件设置显示样式 (一劳永逸的解决办法)
- #settings.py文件设置
- USE_L10N = False  #更换为False
- DATETIME_FORMAT = 'Y-m-d H:i:s' #添加
- #也可以日期与时间分开设置  根据自己的需求设置
- DATE_FORMAT = 'Y-m-d'   
- TIME_FORMAT = 'H:i:s'

safe 防止xss攻击 作用 取消转义
-     XSS攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,
- 故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。
-     XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。
- 比如这些代码包括HTML代码和客户端脚本。攻击者利用XSS漏洞旁路掉访问控制——例如同源策略(same origin policy)。
- 这种类型的漏洞由于被骇客用来编写危害性更大的网络钓鱼(Phishing)攻击而变得广为人知。对于跨站脚本攻击,
- 骇客界共识是:跨站脚本攻击是新型的“缓冲区溢出攻击“,而JavaScript是新型的“ShellCode”。

safe 主要作用
- #Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。
- 但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,
- 这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。
- 为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。
- #页面代码:
- {% load my_tags %}
- {{ 'https://www.baidu.com/'|show:'百度' }}
- #自定过滤器代码
- @register.filter
- def show(url,name):
-     return "<a href = '{}'>{}</a>".format(url,name)

- 过滤器函数 加is_safe = True 解除转义
- #自定过滤器代码
- @register.filter(is_safe = True)
- def show(url,name):
-     return "<a href = '{}'>{}</a>".format(url,name)
- 页面效果
  
 
- 模板语句中加 safe 解除转义 
- #模板语句
- {% load my_tags %}
- {{ 'https://www.baidu.com/'|show:'百度'|safe }}
- 页面效果
  
 
- 也可以导入模块实现这种效果


自定义 filter
- 当普通的内置过滤器,实现不了我们的开发需求,那我们可以自定义过滤器来实现功能
- 自定义过滤器是只能带有一个或两个参数的Python函数:
- 变量(输入)的值 - -不一定是一个字符串
- 参数的值 - 这可以有一个默认值,或完全省略
- 例如,在过滤器{{var | foo:“bar”}}中,过滤器foo将传递变量var和参数“bar”。
- 自定义filter代码文件摆放位置:
- app01/
-     __init__.py
-     models.py
-     templatetags/  # 在app01下面新建一个package package
-         __init__.py
-         app01_filters.py  # 建一个存放自定义filter的py文件
-     views.py
自定义过滤器流程
- 1 在app下创建一个名为templatetags的python包
- #注意 
- 在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag
- (模块名只能是templatetags)
- from django import template
- register = template.Library()  #固定写法,不可改变
- @register.filter  #过滤器
- def add_str(value, arg):  # 最多有两个
-     return '{}-{}'.format(value, arg)
- 5 写页面代码 在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py
- {% load my_tags %}   #先导入我们自定义那个文件 my_tags
- {{ name|add_str:age }}   #参数只能是两个,一个参数是变量name ,一个是参数是后面的那个参数age

- #过滤器函数
- @register.filter
- def multiply1(value,arg):
-     return value * arg
-     
- #模板语句
- {% load my_tags %}
- <p>{{ 6|multiply1:'6' }}</p>
- <p>{{ 6|multiply1:6 }}</p>
- <p>{{ '10'|multiply1:5 }}</p>


- #过滤器函数
- @register.filter
- def division(value,arg):
-     return value / arg
- #模板语句
- {% load my_tags %}
- <p>{{ 6|division:6 }}</p>
- <p>{{ 6|division:1 }}</p>

- #过滤器函数
- @register.filter
- def add(value,arg):
-     return value + arg   #可以这样写,但是转换不了会报错 int(value) + int(arg)
- #模板语句
- {% load my_tags %}
- <p>{{ 6|add:6 }}</p>
- <p>{{ 6|add:0 }}</p>
- <p>{{ '钢蛋g'|add:18 }}</p>


- #过滤器函数
- @register.filter
- def add(value,arg):
-     return value - arg
- #模板语句
- {% load my_tags %}
- <p>{{ 6|add:6 }}</p>
- <p>{{ 6|add:0 }}</p>

自定义标签 simpletag
- 与自定义过滤器区别,可以接受更多参数,使用标签引用{{% %}
- 与自定义filter类似(也是在python包的templatetags文件下,创建此标签),只不过接收更灵活的参数。
- #simple_tag  代码
- @register.simple_tag(name="plus")
- def plus(a, b, c):
-     return "{} + {} + {}".format(a, b, c)
- #使用simple tag  自定义标签 
- {% load app01_demo %}
- {# simple tag #}
- {% plus "1" "2" "abc" %}
-     
-     
- #例子  标签代码
- @register.simple_tag
- def join_str(*args, **kwargs):
-     return '{} - {} '.format('*'.join(args), '$'.join(kwargs.values()))
- # 模板
- {% load my_tags %}
- {% join_str '1' '2' '钢蛋' k1='3' k2='4' %}    

自定义标签 inclusion_tag









- 自定义过滤器 filter 只能接受两个参数 调用的时候使用 {{ filter }}
- 自定义标签  simpletag  与自定义过滤器区别,可以接受更多参数,使用标签引用{{%  %}
-                                             
- 自定义标签 inclusion_tag  多用于返回html代码片段,使用标签引用{{% %}                                   
csrf_token 跨站请求伪造保护
- 在页面的form表单里面写上{% csrf_token %}


静态文件相关
- #作用 在配置文件找到静态文件别名,与文件地址进行拼接,这样别名改了,也不会影响,静态文件的导入
- {% load static %}
- <img src="{% static "images/hi.jpg" %}" alt="Hi!" />
- #引用JS文件时使用:
- {% load static %}
- <script src="{% static "mytest.js" %}"></script>
- #某个文件多处被用到可以存为一个变量
- {% load static %}
- {% static "images/hi.jpg" as myphoto %}
- <img src="{{ myphoto }}"></img>
- {% load static %}
-  <link rel="stylesheet" href="{% static '/plugins/bootstrap-3.3.7/css/bootstrap.css' %}">
-  <link rel="stylesheet" href="{% static '/css/dsb.css' %}">
- {% static '/plugins/bootstrap-3.3.7/css/bootstrap.css' %}  
- {% get_static_prefix %}   # 获取别名


母版和继承
什么是母版?
- 普通的HTML页面 母版页用于处理html页面相同部分内容,避免出现冗余代码,减少重复html页面的编写,提高代码复用性,方便代码修改.
继承写法
- #在子页面中在页面最上方使用下面的语法来继承母板。
- {% extends '母版文件名.html' %}
block 块
- 通过在母板中使用{% block  xxx %}来定义"块"。
- 在子页面中通过定义母板中的block名来对应替换母板中相应的内容。
- #定义block
- {{% block 块名 %}} 
- {{% endblock  %}}
子页面替换母版 block块

- 注意的点:
- 1. {% extends 'base.html' %} 写在第一行   前面不要有内容 有内容会显示
- 2. {% extends 'base.html' %}  'base.html' 加上引号   不然当做变量去查找
- 3. 把要显示的内容写在block块中
- 4. 定义多个block块,定义 css  js 块
组件
- #作用
- 可以将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要使用的地方按如下语法导入即可。
- #示列  单独建一个html  page
- <nav aria-label="Page navigation">
-   <ul class="pagination">
-     <li>
-       <a href="#" aria-label="Previous">
-         <span aria-hidden="true">«</span>
-       </a>
-     </li>
-     {% for i in num %}
-         <li><a href="#">{{ i }}</a></li>
-     {% endfor %}
-     <li>
-       <a href="#" aria-label="Next">
-         <span aria-hidden="true">»</span>
-       </a>
-     </li>
-   </ul>
- </nav>
- #别的页面 引用
- {% include 'page.html' %}