10.Django(标模、模板继承、组件、静态文件)
-
引子
后端有一个可迭代对象,列表list,元素不固定,将列表里面的每个元素都展示到前端。
-
重新创建一个流程
-
def index(request):
name = ['名字1', '名字2', '名字3', '名字4']
return render(request, 'index.html', {'lis': name})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>测试 测试 测试</h1>
<ul>
<li>{{lis.0}}</li>
<li>{{lis.1}}</li>
<li>{{lis.2}}</li>
<li>{{lis.3}}</li>
</ul>
</body>
</html>这样写太low,代码写死了,没有拓展性。我们应该根据后端的可迭代对象里面的数据的个数,动态创建前端标签,然后把数据成功替换。Django框架功能强大,它的模板系统已经提供了类似的功能,这个功能就是模板系统里面的另一个功能:标签tags
-
语法:
他是支持在前端写for循环以及if判断的。
{% for foo in 可迭代互相 %}
逻辑部分
{% endfor %} -
for标签
-
简单示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% for i in lis %}
<li>{{ i }}</li>
{% endfor %}
</ul>
</body>
</html> -
反向循环
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% for i in lis reversed %}
<li>{{ i }}</li>
{% endfor %}
</ul>
</body>
</html> -
循环字典
# 打印所有的键名
<ul>
{% for key in dic %}
<li>{{ key }}</li>
{% endfor %}
</ul>
# 打印所有的键名
<ul>
{% for key in dic.keys %}
<li>{{ key }}</li>
{% endfor %}
</ul>
# 打印所有的值
<ul>
{% for v in dic.values %}
<li>{{ v }}</li>
{% endfor %}
</ul>
# 打印所有的键和值,并组成键值对输出
<ul>
{% for k,v in dic.items %}
<li>{{ k }}:{{ v }}</li>
{% endfor %}
</ul>
-
其他功能
模板系统提供的for循环还提供了一个辅助功能,forloop功能。
forloop.counter 当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能
forloop.counter0 当前循环的索引值(从0开始)
forloop.revcounter 当前循环的倒序索引值(从1开始)
forloop.revcounter0 当前循环的倒序索引值(从0开始)
forloop.first 当前循环是不是第一次循环(返回布尔值)
forloop.last 当前循环是不是最后一次循环(返回布尔值)
forloop.parentloop 本层循环的外层循环的对象,在通过上面的几个属性来显示外层循环的计数等。测试:
{% for i in lis %}
<div>{{ forloop.counter }}{{ i }}</div>
{% endfor %}
{% for i in lis %}
<div>{{ forloop.counter0 }}{{ i }}</div>
{% endfor %}
{% for i in lis reversed %}
<div>{{ forloop.revcounter }}{{ i }}</div>
{% endfor %}
{% for i in lis reversed %}
<div>{{ forloop.revcounter0 }}{{ i }}</div>
{% endfor %} -
for ... empty ... 组合
循环的对象如果有内容,不执行empty语句,如果为空,执行empty语句。
{% for i in lis %}
<div>{{ i }}</div>
{% empty %}
<div>很遗憾,没有查询到内容</div>
{% endfor %}
-
-
if标签
-
语法
{% if 条件 %} 结果 <!-- 不满足条件,不会生成这个标签 --> {% elif 条件 %} 结果 {% else %} <!--也是在if标签结构⾥⾯的--> 结果 {% endif %}
-
测试
-
示例
{% if age == 16 %} <div>正式花季年龄</div> {% elif age == 17 %} <div>正式雨季年龄</div> {% else %} <div>不是花雨季年龄</div> {% endif %}
-
判断条件可以与过滤的功能想结合
{% if lis|length > 4 %} <div>列表元素超过了4个</div> {% else %} <div>列表元素不足4个</div> {% endif %}
-
条件也可以加逻辑运算符
if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,注意条件两边都有空格。
{% if lis|length > 4 and '名字1' in lis %} <div>列表元素超过了4个,并且“名字1”在列表里面</div> {% else %} <div>列表元素不足4个</div> {% endif %}
-
-
-
with标签
类似于起别名。变量名字过长或者变量需要昂贵的代价获取时,我们需要起别名。
# 方式1:等号两边不要用空格 {% with vrs=voice_recognition_system %} <div>{{ vrs }}</div> <p>{{ vrs }}</p> {% endwith %} # 方式2: {% with voice_recognition_system as vrs %} <div>{{ vrs }}</div> <p>{{ vrs }}</p> {% endwith %} # 比较昂贵的方式 {# 前端通过sql想做进⼀步加⼯,获取总个数 #} {#{{ sql.count }}#} {#<div>{{ sql.count }}</div>#} {#<div>{{ sql.count }}</div>#} {% with stu_num=sql.count %} <div>{{ stu_num }}</div> <div>{{ stu_num }}</div> <div>{{ stu_num }}</div> <div>{{ stu_num }}</div> {% endwith %}
-
for标签与id标签的结合使用
-
示例
forloop.first 以及 forloop.last都是返回的bool值
{% for i in lis %} {% if forloop.first %} <div>第一个元素为{{ i }}</div> {% else %} <div>{{ forloop.first }}</div> {% endif %} {% endfor %}
forloop.parentloop:一定注意!他是返回本次循环外层循环对象,这个对象可以调用forloop的各种方法进行获取响应的数据。
lis1 = (['A', 'B'], ['C', 'D'], ['E', 'F']) {% for i in lis1 %} {% for j in i %} <div>{{ forloop.parentloop.counter }}{{ j }}</div> {% endfor %} {% endfor %}
-
-
注意事项
1、Django的模板语言不支持连续判断。即不支持一下写法:
{% if a > b > c %} ... {% endif %}
2、Django的模板语言中属性的优先级大于方法(了解)
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的intems key的值。
{{ dic.items }} 只能获取键对应的值,⽽不能执⾏下⾯的代码 {% for key,values in dic.items %} <div>{{ key }}: {{ values }}</div> {% endfor %}
-
csrf_token标签
我们之前post提交数据时,遇到forbbien错误,当时我们是把setting里面的csrf中间件注销,这样解决不合理,这个一个安全机制。
重新构建一个流程:
def login(request): if request.method == 'POST': print(request.POST.get('username')) print(request.POST.get('password')) return HttpResponse('登录成功') return render(request, 'login.html')
就是因为这个中间件:csrf_token安全机制
重写HTML页面:
<form action="" method="post"> {% csrf_token %} ⽤户名:<input type="text" name="username"> <p></p> 密码:<input type="text" name="password"> <p></p> <input type="submit"> </form>
再次通过get请求请求页面,执行到views函数,通过render渲染了html页面,最后通过return将最终的渲染过后的html页面返回个浏览器,那么render将{% csrf_token %}渲染成了什么??
渲染蹭了一个隐藏的input标签
<input type='hidden' name='csrfmiddlewaretoken' value='gYY7y7TbmUzRp5yHaAtiFqLwUO61LvacYZrhzz8lrU5kAqsTI1buvrnFU23QGBb3' />
我们填写完用户名、密码之后。再次提交,直接提交成功了。
过程:
并且每次请求登录页面时,隐藏的input标签对应的value值是随机的密文。
当我们加上此标签之后,再次发出get请求,render返回给我们页面中多了一个隐藏的input标签,并且这个标签里面有一个键值对:
键:name,值:随机的⼀堆密⽂。 那么这个是干什么用的呢?其实他的流程是这样的:
第一次发送get请求,在views视图函数返回给你login.html页面你之前,render会将csrf_token标签替换成一个隐藏的input标签,此标签的键值对就是上面那么键值对并且Django将这个键值对保存在内存;当你再次进行post请求时,他会验证你的form表单里面的隐藏的input标签的键值对是否与我内存中存储的键值对相同,如果相同,你是合法的提交,允许通过;如果不相同,则直接返回给你forbidden页面。这就好比说,第一次get请求,他返回给你一个post请求时,必须是从我给你的get请求返回的页面提交的。
为什么这么做呢?是因为有人登录你的页面时是可以绕过get请求,返回的页面直接进行post请求登录的,比如说爬虫。直接通过requests.post('/login/')直接请求的,这样是没有csrftoken的,这样就直接拒绝了。
利用爬虫测试:
ret = requests.post( 'http://127.0.0.1:8000/login/', data={'username': 'taibai', 'password': 123} ) print(ret.content.decode('utf-8'))
模板继承
-
引子
模板+继承,什么是模板?render渲染一下html页面,形成最终的html页面交由return返回给浏览器,什么是继承?继承为面向对象的三大特性之一,子继承父。模板系统在加上继承的特性。
-
需求:我们要写四个页面,这四个页面共同好友的内容顶部导航栏以及左侧菜单栏。中间的内容每个页面不同。
1、重新创建一个项目:
2、分别构建不同的部分
urls:
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^base/', views.base), url(r'^menu1/', views.menu1), url(r'^menu2/', views.menu2), url(r'^menu3/', views.menu3), ]
views:
from django.shortcuts import render # Create your views here. def base(request): return render(request, 'base.html') def menu1(request): return render(request, 'menu1.html') def menu2(request): return render(request, 'menu2.html') def menu3(request): return render(request, 'menu3.html')
base页面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Bootstrap 101 Template</title> <style> body{ margin: 0; padding: 0; } .nav{ width: 100%; height: 50px; background-color: black; color: white; line-height: 50px; } .nav a{ color: white; text-decoration: none; margin-right: 25px; } .sidebar{ float: left; width: 25%; background-color: #575757; text-align: center; color: white; height: 1000px; } .sidebar a{ color: white; text-decoration: none; } .menu{ float: right; width: 75%; } </style> </head> <body> <div class="nav"> <a href="">普通⼤堂</a> <a href="">盆⼉堂</a> <a href="">搓澡</a> <a href="">捏脚刮痧拔罐</a> <a href="">推油</a> <a href="">特叔服务</a> <input type="text"> ⾼级检索 </div> <div class="sidebar"> <ul type="none"> <li><a href="/menu1/">菜单⼀</a></li> <li><a href="/menu2/">菜单⼆</a></li> <li><a href="/menu3/">菜单三</a></li> </ul> </div> <div class="menu"> base的⾸⻚ </div> </body> </html> # 其他页面只有menu内容变化了其他没变
我们通过分析这些html页面,发现了大量的重复代码,太low了,有没有一种解决方式简化我们的代码呢?
3、母版示例
创建一个模板index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Bootstrap 101 Template</title> <style> body{ margin: 0; padding: 0; } .nav{ width: 100%; height: 50px; background-color: black; color: white; line-height: 50px; } .nav a{ color: white; text-decoration: none; margin-right: 25px; } .sidebar{ float: left; width: 25%; background-color: #575757; text-align: center; color: white; height: 1000px; } .sidebar a{ color: white; text-decoration: none; } .menu{ float: right; width: 75%; } </style> </head> <body> <div class="nav"> <a href="">普通⼤堂</a> <a href="">盆⼉堂</a> <a href="">搓澡</a> <a href="">捏脚刮痧拔罐</a> <a href="">推油</a> <a href="">特叔服务</a> <input type="text"> ⾼级检索 </div> <div class="sidebar"> <ul type="none"> <li><a href="/menu1/">菜单⼀</a></li> <li><a href="/menu2/">菜单⼆</a></li> <li><a href="/menu3/">菜单三</a></li> </ul> </div> <div class="menu"> </div> </body> </html>
其余的四个页面全都继承模板,在页面的最顶端写上:
{% extends 'index.html' %}
4、自定制效果
我们不能所有的内容都继承父类的内容,每个页面都应该有一些自己的个性化的设置。我们可以在母版的相应的位置设置钩子block,然后在不同的页面进行相应的替换。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Bootstrap 101 Template</title> <style> body{ margin: 0; padding: 0; } .nav{ width: 100%; height: 50px; background-color: black; color: white; line-height: 50px; } .nav a{ color: white; text-decoration: none; margin-right: 25px; } .sidebar{ float: left; width: 25%; background-color: #575757; text-align: center; color: white; height: 1000px; } .sidebar a{ color: white; text-decoration: none; } .menu{ float: right; width: 75%; } </style> </head> <body> <div class="nav"> <a href="">普通⼤堂</a> <a href="">盆⼉堂</a> <a href="">搓澡</a> <a href="">捏脚刮痧拔罐</a> <a href="">推油</a> <a href="">特叔服务</a> <input type="text"> ⾼级检索 </div> <div class="sidebar"> <ul type="none"> <li><a href="/menu1/">菜单⼀</a></li> <li><a href="/menu2/">菜单⼆</a></li> <li><a href="/menu3/">菜单三</a></li> </ul> </div> <div class="menu"> {% block content %} {% endblock content %} </div> </body> </html> 然后去相应的html文件中写入: {% block content %} <h1>你想写的内容</h1> 备注:可以加标签页可以不加标签 {% endblock content %}
钩子不仅仅是可以放在html标签中,还可以放在css,js代码中,只要是前端页面内容都可以设置钩子。我们以css举例:
index.html页面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Bootstrap 101 Template</title> <style> body{ margin: 0; padding: 0; } .nav{ width: 100%; height: 50px; background-color: black; color: white; line-height: 50px; } .nav a{ color: white; text-decoration: none; margin-right: 25px; } .sidebar{ float: left; width: 25%; background-color: #575757; text-align: center; color: white; height: 1000px; } .sidebar a{ color: white; text-decoration: none; } .menu{ float: right; width: 75%; } {% block title %} {% endblock title %} </style> </head> <body> <div class="nav"> <a href="">普通⼤堂</a> <a href="">盆⼉堂</a> <a href="">搓澡</a> <a href="">捏脚刮痧拔罐</a> <a href="">推油</a> <a href="">特叔服务</a> <input type="text"> ⾼级检索 </div> <div class="sidebar"> <ul type="none"> <li><a href="/menu1/">菜单⼀</a></li> <li><a href="/menu2/">菜单⼆</a></li> <li><a href="/menu3/">菜单三</a></li> </ul> </div> <div class="menu"> {% block content %} {% endblock content %} </div> </body> </html>
base.html:
{% extends 'index.html' %} {% block content %} <h1>你想写的内容</h1> {% endblock content %} {% block title %} .nav{ background-color:red; } {% endblock title %}
5、保留母版内容并添加个性化内容
index母版页面:
<div class="menu"> {% block content %} <div>这是母版</div> {% endblock content %} </div>
只是单独给menu1页面这样设置:
{% extends 'index.html' %} {% block content %} {{ block.super }} 菜单1 首页 {% endblock content %}
其他页面不变,对比看一下结果
6、总结
-
如果你在模板中使用
{% extends %}
标签,它必须是模板中的第一个标签。 -
模板中设置的钩子可以在css、js、标签是哪个部分设置,越多越好
-
结束一个钩子时可以标注次钩子的名字,比如
{% endblock content %}
这样就可以结束此钩子不至于将其他的block钩子一并结束。
-
组件
模板阶乘一般都是继承主体框架,这里面我们可以分为是三部分,导航条,左侧菜单栏,右侧自定制,那么这三部分就好比是三个组件,模板继承与组件的原理相同,但是他们的格局以及体量是不同的额,模板是由大大小小的多个组件组成的固定搭配,也可以成为前端主体框架,而我们的组件就是一组一组细化的功能。
我们写一个导航栏的组件:nav.html
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <style> body{ margin: 0; padding: 0; } .nav{ width: 100%; height: 50px; background-color: black; color: white; line-height: 50px; } .nav a{ color: white; text-decoration: none; margin-right: 25px; } </style> </head> <body> <div class="nav"> <a href="">普通⼤堂</a> <a href="">盆⼉堂</a> <a href="">搓澡</a> <a href="">捏脚刮痧拔罐</a> <a href="">推油</a> <a href="">特叔服务</a> <input type="text"> ⾼级检索 </div> </body> </html>
测试一个页面sub.html。去引用这个组件
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% include 'nav.html' %} <h1>你好,世界</h1> </body> </html>
静态文件
css、js是需要单独文件存放的,还有bootstrap的插件,jQuery的插件,视频,图片,音频等等文件,应该统一放在一个目录下,那么我完成一个静态文件的设置。
1、重新创建一个项目static_pro,创建一个专门放置静态文件的文件夹。
2、构建css文件以及html页面:
body { margin: 0; padding: 0; } .nav { width: 100%; height: 50px; background-color: black; color: white; line-height: 50px; } .nav a { color: white; text-decoration: none; margin-right: 25px; } <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div class="nav"> <a href="">普通⼤堂</a> <a href="">盆⼉堂</a> <a href="">搓澡</a> <a href="">捏脚刮痧拔罐</a> <a href="">推油</a> <a href="">特叔服务</a> <input type="text"> ⾼级检索 </div> <h1>你好,世界</h1> </body> </html>
你会发现,请求失败了。
我们现在基于Djangp给我们提供的框架构建项目,一切就要按照Django的要求来,你要想引用静态文件,必须在settings里面设置配置。
3、配置srttings
STATIC_URL = '/static/' #要是没有这个就要补上 STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'jingtaiwenjian'), # 静态⽂件名随便起 ]
4、更改index页面的引用css的方式:
<link rel="stylesheet" href="/static/nav.css">
5、别名static
为什么要起别名?
-
使用别名代替真实的静态文件名字,防止有人对你的静态文件发起恶意攻击,保证静态文件的安全。
我们通过这个用别名加密的路径可以直接访问资源:
我们使用别名伪装的路径可以获取到相应的文件,但是通过这个伪装的路径不能对文件进行破坏,因为路径是通过别名伪装的,假的。
-
使用别名为了具有拓展性。
假如你的项目静态文件的名称改掉了,如果说你不是用别名引用的文件,而是真实的路径,凡是涉及到所有的应用的路径全部都需要更改。
<link rel="stylesheet" href="/jingtaiwenjian/nav.css"> <link rel="stylesheet" href="/jingtaiwenjian/nav.js"> <link rel="stylesheet" href="/jingtaiwenjian/jquery.js"> ......
-