一. 前言
Django模板层的知识包括标签,过滤器,自定义标签,自定义过滤器以及inclusion_tag,最重要的是模板的继承和导入,模板层最重要的是模板语法,之前我们涉及到的变量用模板语法{{}},涉及到逻辑用模板语法{% %},这里我们在追加几点,过滤器在模板语法{{ }}中写,而且只能传两个参数;标签在模板语法{% %}中写,且能传多个参数(参数之间用空格隔开).模板的继承和导入也是在模板语法{% %}中写.
接下来我们回顾下后端朝前端传递数据的两种方式:
from django.shortcuts import render
# Create your views here.
def login(request):
'''模板语法只有2个:
渲染变量:{{}} 1.深度查询--->>句点符;2.过滤器
渲染标签:{% %}
:param request:
:return:
'''
name = "panshao"
i = 10 #整型
l = [1,2,3] #列表
info = {"name":"panshao", "age":22} #字典
b = True #布尔
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
alex = Person("alex",33) #对象
egon = Person("egon",38)
person_list = [alex, egon]
#return render(request, "login.html", {"name":name})
return render(request, "login.html", locals()) #locals是局部变量的意思,这样就不用一个个去对应了;它会把这些局部变量直接传给模板,它传的时候就是按照{“name”:name} {"alex":alex}这种方式传的
login.py
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>index</h1>
<p>{{ name }}</p>
<p>{{ i }}</p>
<p>{{ info }}</p>
<p>{{ l }}</p>
<p>{{ alex }}</p>
<p>{{ person_list }}</p>
<p>{{ b }}</p>
<hr>
深度查询 是用 . 完成的
<p>{{ l.1 }}</p> #我想要第2个元素
<p>{{ info.name }}</p> #字典下的name那个键
<p>{{ alex.age }}</p> #alex这个变量对象
<p>{{ person_list.1.age }}</p> #person_list中第二个人的年龄
</body>
</html>
两种方法有利有弊,不过在之后我们会选用locals(),后端除了能向前端提交的Python的基本的数据类型: int, float, str, list, dict, tuple, set数据外,还能提交函数与类的对象,当我们传递函数名给前端时,会自动拿到该函数return的结果, 但是不支持传参,只支持传递无参函数.当传递类的对象obj给前端时,相当于print (obj),前端拿到的是该obj的内存地址,因为print(obj)走的是类的__str__()方法.所以传递类对象时,我们重新写__str__()能让前端拿到想要的内容. 前端想要获取后端传递过来的容器类型中的元素时,统一使用句点符(.):
二. 过滤器
过滤器在模板语法{{ }}中书写,过滤器最多支持传两个参数,(会把|左边的参数当做过滤器的第一个参数传进去),过滤器的基本语法: {{ 参数1|过滤器名字: 参数2}}, 有些过滤器没有参数2
统计字符串长度{{ s|length}}: 会将s当做length的参数传进去,将s的长度打印出来
获取的数据为空时就返回default后面定义的值{{ s|default: 's是空的字符串'}}, 这种方法跟后端的get方法很像,default必须要有两个参数
将数字转化为表示文件大小的格式{{ file_size|filesizeformat}} 给file_size一串数字,会转化为多少兆,多少G之类的,如果file_size是204800,执行该过滤器后就是200kb
格式化时间{{ ctime|date: 'Y-m-d'}}不需要和后端一样加% ('%Y-%m-%d'),后端代码为
from datetime import datetime
ctime = datetime.now()
字符串的切片操作{{s|slice:'0: 8: 2'}},如果s='hello world',执行完该过滤器后是'hlow', slice后面的参数同python字符串切片参数一样,开头: 结尾: 步长,切片顾头不顾尾
截取固定长度的字符串{{s|truncatechars:10}} 如果s='hello world' 执行完该过滤器后是'hello w...', 超出长度的部分会用...代替,当我们的参数为10时, 实际上截取的字符串长度为7,因为...占3位.
按照空格截取文本内容{{s|truncatewords:2}} 如果s='he llo wor ld',执行完该过滤器后是:‘he llo ...’,截取至前两个空格,之后的内容用...表示。
加法亦或是字符串拼接{{ n1|add:n2}} 如果n1=2, n2=4,那么结果为6。如果n1='hello', n2='world',那么结果为'helloworld'
当我们后端向前端提交的字符串含有标签时,比如'<h1>this is title</h1>',为了防止脚本攻击(比如<script>while(1){alert('come on')}</script>),前端之后把你当成普通的字符串。当我们想要让前端帮我们执行提交的字符串中的标签内容时,有两种方式。
1. 在前端通过{{字符串|safe}}告诉前端字符串很安全,可以大胆执行
2. 后端用模块操作一下字符串,告诉前端我们给它的字符串很安全。
from django.utils.safestring import mark_safe def login(request): html = reverse('login') s = '<h1>this is title</h1>' s = mark_safe(s) return render(request, 'login.html', locals()) # 前端直接{{ s }}即可
三. 标签
标签在模板语法中{% %}书写, {% %}涉及到逻辑,标签一般是for循环,f判断以及它们的嵌套使用,除此之外还有empty标签。
for 循环中可以使用forloop来给我们提供一些信息:
for循环
<!--l = [1, 2, 3, 4, 5]-->
{% for foo in l %}
<p>{{ foo }}</p>
<p>{{ forloop }}</p>
{% endfor %}
if判断
{% if flag %}
<p>flag不为空</p>
{% else %}
<p>flag是空</p>
{% endif %
if与for嵌套使用
{% for foo in s %}
{% if forloop.first %}
<p>这是第一次</p>
{% elif forloop.last %}
<p>这是最后一次</p>
{% else %}
<p>keep up</p>
{% endif %}
{% endfor %}
empty
{#{% for foo in '' %}#}
{# {% if forloop.first %}#}
{# <p>这是我的第一次</p>#}
{# {% elif forloop.last %}#}
{# <p>这是最后一次了啊</p>#}
{# {% else %}#}
{# <p>来啊来啊!!!</p>#}
{# {% endif %}#}
{# {% empty %}#}
{# <p>当for循环的对象为空的时候 会走empty</p>#}
{#{% endfor %}#}
四. 自定义inclusion_tag
要自定义过滤器与标签,必须先做三件事
- 在应用文件夹下新建templatetags文件夹(必须叫这个名字)
- 在templatetags文件夹中新建一个py文件,名字任意,不过最好不要有中文
- 在该py文件中固定写以下两句代码
from django import template register = template.Library()
自定义inclusion_tag
from django import template
register = template.Library()
@register.inclusion_tag('login.html', name='login')
def login(n):
l = ['第%s项' % i for i in range(n)]
return {'l': l}
<!--login.html-->
<ur>
{% for foo in l %}
<li>{{ foo }}</li>
{% endfor %}
</ur>
在该例子中, 我们在前端调用inclusion_tag时:
{% load my_tags %}
{% login 5 %}
首先会将参数5传给login函数,函数执行完后的结果提交至login.html,然后将login.html的页面显示在调用该inclusion_tag的地方。
inclusion_tag会将渲染过的login.html界面返回至调用它的位置,正是因为这种机制,所以它所渲染那个html页面中可以不需要head和body等各种标签,你需要返回什么就在里面写什么。
五. 模板的继承
1. 模板的继承
首先需要被继承的模板中划分多个区域:
{% block 给区域起的名字 %}
{% endblock %}
通常情况下一个模板至少需要三块区域:
{% block css %}
页面css代码块
{% endblock %}
{% block js %}
页面js代码块
{% endblock %}
{% block content %}
页面主体内容
{% endblock %}
子模板继承模板,首先要继承所有的内容:
{% extends 'home.html' %}
{#先继承home,必须写在首行,不然它不知道block是什么意思 跟include不一样,它有它自己独特的优势#}
根据被block快的名字修改指定区域的内容
{% block content %}
<h1>登录页面</h1>
<form action="">
<p>username:<input type="text" class="form-control"></p>
<p>password:<input type="text" class="form-control"></p>
<input type="submit" class="btn btn-success">
</form>
{% endblock %}
可以使用{{block.super}}使用父模板的该block的内容(使用该方法没有提示,要手撸)
{% block content %}
{{ block.super }}
{% endblock %}
注意:原模板中的block区域越多越好,因为越多代表可以修改的细节很多,扩展性就越强。继承后,子模板只能修改block区域中的内容。
2. 静态文件配置
当我们为了让路由名字改变时我们不需要修改HTML页面和视图函数中的内容,我们引入了反向解析。同理,为了防止settins.py文件夹中static文件夹的接口前缀改变而我们不需要html页面中head中script和link的接口前缀(需要修改的前提是他们导入的是static文件夹中的东西),我们引入了静态文件配置。
{% load static %}
<link rel='stylesheet' href="{% static 'css/mycss.css'%}"> # 第一种方式
<link rel='stylesheet' href="{% get_static_prefix %}css/mycss.css"> # 第二种方式