6.Flask轻量型框架
6.1Flask简介
python提供的框架中已经写好了一个内置的服务器,服务器中的回应response行和头已经写好,我们只需要自己写显示在客户端,的主体body部分。
----------------------------------------------------------
Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。Flask使用 BSD 授权。
Flask也被称为 “microframework” ,因为它使用简单的核心,用 extension 增加其他功能。Flask没有默认使用的数据库、窗体验证工具。
----------------------------------------------------------
WSGI接口协议:是python应用程序或框架和web服务器之间的一种接口
6.2安装
在虚拟环境的基础上 cmd打开命令行 workon py1901
执行 pip install Flask (自动把Werzeug和Jinja2安装捆绑安装)
查看 pip list
6.3MVT
python提供的框架中已经写好了一个内置的服务器,服务器中的回应response行和头已经写好,我们只需要自己写显示在客户端,的主体body部分。
主体body中 既有css、js 又有 数据库、python,所以mvt在主体中将他们分成了三个部分
-------------------------------------------------------------
mvt:
model-------->数据库
view--------->视图 url路由 |python代码
template----->模板 html网页 在静态页面中可以写python代码
6.4Flask的初始化方法
def __init__(
self,
import_name,
static_url_path=None,
static_folder='static',
static_host=None,
host_matching=False,
subdomain_matching=False,
template_folder='templates',
instance_path=None,
instance_relative_config=False,
root_path=None
):
6.5路由
from flask import Flask
import settings
app = Flask(__name__)
app.config.from_object(settings)
#路由设置
@app.route('/')
def hello_world():
return 'Hello Baby! 啦啦啦啦啦啦 app1'
@app.route('/user/login/')
def login():
return '用户正在登陆.....'
@app.route('/user/register/')
def register():
return '用户正在注册.....'
def order():
print('您正在查看订单.....')#打印在控制台的
return '您正在查看订单.....'#打印在网页的
app.add_url_rule('/orders',view_func=order)
if __name__=='__main__':
print(app.url_map)
app.run(port=8080)
#settings.py文件中:
ENV = 'development'
DEBUG = True
可以看到响应的结果:
通过以上 我们可以看出 —— 定义路由的方式有两种:
#1.使用装饰器
@app.route('/user/login/')
def login():
return '用户正在登陆.....'
@app.route('/user/register/')
def register():
return '用户正在注册.....'
#2.通过add_url_rule实现
def 函数名():
return '您正在查看订单.....'#打印在网页的
app.add_url_rule('路由规则',view_func=函数名)
'''
路有规则就是网址后面那个路径
view_func=函数名 函数名后面不加括号
'''
def order():
return '您正在查看订单.....'#打印在网页的
app.add_url_rule('/orders',view_func=order)
6.6路由的变量规则
=========================================================
默认的就是字符串规则
=========================================================
/<变量>/
from flask import Flask import settings app = Flask(__name__) app.config.from_object(settings) @app.route('/tieba/<keyword>/') #keyward是一个变量 代表着你给我什么我就传什么 def tieba(keyword):#有<>出现了 路由的视图函数里就必须有一个重名的参数出现 return '正在看%s' % keyword if __name__=='__main__': print(app.url_map) app.run(port=8080
=========================================================
int规则
还要设置一个第几页:这里涉及了一个int规则 ———— int:
from flask import Flask import settings app = Flask(__name__) app.config.from_object(settings) #如果不加int的话 page默认的类型就是字符串类型 @app.route('/tieba/<keyword>/<int:page>') #keyward是一个变量 代表着你给我什么我就传什么 def tieba(keyword,page): #有<>出现了 路由的视图函数里就必须有一个重名的参数出现 return '正在看%s,当前是第%d页' % (keyword,page) if __name__=='__main__': print(app.url_map) app.run(port=8080)
注释:当装饰器带参数的时候
def zhuang1(wifename):
def inner1(f):
def inner2(*args,**kwargs):
f(*args,**kwargs)
print("媳妇必须像%s" % wifename)
return inner2
return inner1
@zhuang1('贾玲')
def func2():
print("好好学习,努力挣钱,娶媳妇!")
if __name__ == '__main__':
func2()
'''
如果装饰器带参数
必须要定义三层
'''
=========================================================
float规则
rom flask import Flask import settings app = Flask(__name__) app.config.from_object(settings) @app.route('/tieba/<keyword>/<float:score>') def tieba2(keyword,score): str = '正在看的节目是{},评分是{}.2f '.format (keyword,score) #.2f表示的是小数点后保留2位 return str if __name__=='__main__': print(app.url_map) app.run(port=8080)
path规则
from flask import Flask
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/tieba/<path:pathname>')
def tieba2(keyword,pathname):
return pathname
#http://127.0.0.1:8080/tieba/快乐大本营/9a/b
#拿到的值就是:快乐大本营/9a/b
if __name__=='__main__':
print(app.url_map)
app.run(port=8080)
=========================================================
下面介绍的是使用变量规则
''' 使用路由: 1. 变量规则: @app.route('/tieba/<keyword>/<int:page>') def tieba(keyword, page): print(type(keyword)) # 默认str print(type(page)) return '正在看:%s,当前是第%d页' % (keyword, page) 转换器的类型: int float path 默认:str
======================================================
6.7get请求参数传参
from flask import Flask, request
import settings
app = Flask(__name__)
app.config.from_object(settings)
#超链接和直接拼接在地址栏后面参数,使用的都是get请求
# 使用get请求的参数
# http://127.0.0.1:8080/tieba/?keyword=快乐大本营
@app.route('/tieba/')
def tieba():
# get请求
print(request.args) # 请求参数,都存放在args,底层就是一个ImmutableMultiDict字典对象
keyword = request.args.get('keyword')
page = request.args.get('page')
print('page是:'+page)
# keyword = '快乐大本营'
return '正在看:%s' % (keyword)
# http://127.0.0.1:8080/tieba/?keyword=runningman&page=100
if __name__ == '__main__':
print(app.url_map)
app.run(port=8080)
控制台输入 page=100
6.8Request对象
小知识
dict={} value = dict.get('keyword') print(value) #----->None value2 = dict.get('keyword','nba') print(value2) #----->nba ''' value3 = dict ['keyword'] print(value3) #----->这种方法报错 '''
先定义一个空字典 取不出来值的话就是none,如果有默认值就是nba
request对象的源代码
current_app = LocalProxy(_find_app) request = LocalProxy(partial(_lookup_req_object, 'request')) session = LocalProxy(partial(_lookup_req_object, 'session')) g = LocalProxy(partial(_lookup_app_object, 'g'))
from flask import Flask, request import settings app = Flask(__name__) app.config.from_object(settings) @app.route('/f') def hello_world(): #request对象已经产生,request:请求行、请求头、请求体 #http://127.0.0.1:5000/f?kw=abc&page=1 print(request.base_url) #http://127.0.0.1:5000/f print(request.full_path) #/f?kw=abc&page=1 print(request.path) #/f print(request.args) #args就是arguments的缩写 ImmutableMultiDict([('kw', 'abc'), ('page', '1')]) value=request.args.get('kw','nba') return 'Hello World!————>' + value if __name__ == '__main__': app.run()
6.8.1超链接
如果有一个表单该怎么做呢?
在template里new一个tieba.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>tieba</title>
</head>
<body>
<a href="/zy">快乐大本营</a>
{#如果这里没有/ 但是app.py里的zy后面有/ 那么就重定向到runningman#}
<a href="/zy/?kw=欢乐喜剧人">欢乐喜剧人</a>
<a href="/zy/?kw=王牌对王牌">王牌对王牌</a>
<a href="/zy/?kw=极限挑战">极限挑战</a>
<a href="/zy/?kw=奔跑吧兄弟">奔跑吧兄弟</a>
</body>
</html>
app.py文件:
from flask import Flask,render_template,request
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/zy')
def zongyi():
return render_template('tieba.html') #模板渲染
@app.route('/zy/') #/zy后面如果有/ 那么tieba.html里的请求路径的zy后面也要有
def zy():
value = request.args.get('kw','runningman')
return '正在看%s' % value
if __name__ == '__main__':
app.run()
点击欢乐喜剧人:
上面的是超链接页面
接下来我们进入表单页面
6.8.2表单
在template里新建一个comment.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>评论</title>
</head>
<body>
<form action="/comment/" method="post">
{# 1、method="get"方法的使用方式如下:#}
{# http://127.0.0.1:8080/comment=aaa123#}
{# 2、记住!!!开发的时候 所有的表单相关的涉及到提交的#}
{# 都要用post 因为地址栏的长度是有限的 不可能把所有的东西都写在上面#}
{# 也有很多机密性的东西容易被截获 被别人看到#}
{# 所以对于表单的东西都要用post#}
<p><input type="text" name="comment"></p>
<p><input type="submit" value="评论"></p>
</form>
</body>
</html>
app.py文件:
from flask import Flask,render_template,request
import settings
app = Flask(__name__)
app.config.from_object(settings)
#所有超级链接发出的都是GET请求,地址栏上直接输入的也是GET请求
#下面点击按钮的是POST请求
@app.route('/comment/',methods=['POST','GET'])
# 如果想让POST和GET都进入来 需要开门 在app.route里设置
def comment():
print(request.method)
if request.method == 'GET':
return render_template('comment.html')
elif request.method == 'POST':
return '呵呵'
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
上面我们只是回复了一个呵呵 那么我们该怎么接收表单的数据呢?
request.form.get(‘comment’)
from flask import Flask,render_template,request
import settings
app = Flask(__name__)
app.config.from_object(settings)
#所有超级链接发出的都是GET请求,地址栏上直接输入的也是GET请求
#下面点击按钮的是POST请求
@app.route('/comment/',methods=['POST','GET'])
# 如果想让POST和GET都进入来 需要开门 在app.route里设置
def comment():
print(request.method)
if request.method == 'GET':
return render_template('comment.html') #展示给用户的部分
elif request.method == 'POST':
#从post里取值
print(request.form)
values =request.form.get('comment')#获取用户的提交部分
return '呵呵——————>' + values
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
6.8.3request总结
直接导入使用:
request ----> from flask import request
request.args.get() ----> 获取get的请求参数
request.form.get() ----> 获取post的提交数据
request.method ----> 模板(template) ---->render_template(模板名称) 注意@app.route(‘/comment/’,methods=[‘POST’,‘GET’])
request.path
request.full_path
request.cookies ----> 會話機制
request.files ----> 文件上传
6.9Response
response:
响应行 (协议、状态)
响应头 (响应的类型、响应的内容的字节、服务器是什么)
响应体 (给用户的)
四种响应
1、直接return 字符串
直接来一个字符串str 怎么就能变成response对象呢?
虽然反回的是一个字符串 但是底层当中就把字符串放到响应体中 构成
一个完整的response 再把完整的response返回出去
2、return 渲染模板
渲染模板能转成字符串吗?可以 —— render_template_string
from flask import Flask,render_template,request,render_template_string
在template里新建test.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>
<div>
<p><span>哈哈哈哈哈哈</span></p>
</div>
</h1>
</body>
</html>
app.py文件:
from flask import Flask,render_template,request,render_template_string
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/movie')
def movie():
s= render_template('test.html')
return s
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
3、自己建一个 make_response
能转化成一个response对象吗?可以 —— make_response
from flask import Flask,render_template,request,make_response
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/movie')
def movie():
#响应体
response = make_response('喜欢看:惊奇队长')
#有了响应体,就要加响应头
# response.headers ['Server']='Flask Inner Server'
#常用的头:
response.headers ['Cookies'] = ''
#获得默认的服务器名字:Server: Werkzeug/0.14.1 Python/3.6.5
return response
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
4、return 字典对象
Jsonify —— 底层就是用 dumps/序列化 完成的
from flask import Flask,render_template,request,jsonify,json
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/music')
def music():
data = {
'name':'麻烦豆腐',
'author':'张艺兴',
'country':'中国'
}
# return jsonify(data)
return json.dumps(data)
#在网页端的Network我们可以看到:
#jonify之后 把字典转化成了Json类型——Content-Type:application/json
#json.dumps 转为了字符串的类型——Content-Type: text/html; charset=utf-8
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
结果:
jsonify( ):
json.dump( ):
Jonify和Json.dump的区别:
jsonify(data) ---->底层使用 json.dumps(data) —> 转为str ----> response = make_response(str) response.headers[‘Content-Type’]=‘application/json’----> 最后抛出的response就是JSONResponse
json.dumps(data)
他们的关系就是Jsonify使用了json.dumps(data)
总结
总结: response
-
直接 return 字符串
-
render_template(‘模板名称’)
-
make_response(…)
-
jsonify(data)
小知识:列表一样可以被转成字符串或者是json类型
from flask import Flask,render_template,request,jsonify,json
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/music')
def music():
Alist=['Alone','Billion','helloworld']
# return jsonify(Alist)
return json.dumps(Alist)
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
6.10模板 template
模板引擎:Jinjia2
Flask 使用 Jinja 2 作为模板引擎。当然,你也可以自由使用其它的模板引擎,但运行 Flask 本身仍然需要 Jinja2 依赖 ,这对启用富扩展是必要的,扩展可以依赖 Jinja2 存在。
Jinja 2 默认配置如下:
- 所有扩展名为
.html
、.htm
、.xml
以及.xhtml
的模板会开启自动转义 - 模板可以利用
{% autoescape %}
标签选择自动转义的开关。 - Flask 在 Jinja2 上下文中插入了几个全局函数和助手,另外还有一些目前默认的值
{# xml小简介:
所有的标签都是自己写的文件 新建一个test.xml文件 #}
<school>
<name>Echo</name>
<address>1111</address>
<classes>
<class>
<name>python111</name>
</class>
<class>
<name>python222</name>
</class>
<class>
<name>python333</name>
</class>
<class>
<name>python444</name>
</class>
</classes>
</school>
{# xml最大的作用就是:以上面的格式来保存数据 #}
{#
xml和json都是轻量级数据传输方式:
数据量不大 不需要都放在数据库里 直接写在这就可以
json:
{
name:'',
address:''
classes:[
{},
{},
{},
......
]
}
#}
举一个JSON的例子:
打开淘票票网站
右键检查 看Network
然后选择XHR 就是ajax的对象
鼠标往北京那个点一放 发现多了一行
点击那一行 可以看到response里面都是json数据
复制之后打开bejson网站 进行解析
现在都常用JSON 不用xml 为什么?
因为xml里面都是标签 需要封装成一个DOM对象 DOM对象层层去找 就比较麻烦 所以用JSON更方便
渲染模板
{{ name }} ————> 获取渲染的变量的值
render_template(‘base.html’,name=name,name1=name1) # 模板渲染 ——> xxx.html {{name}} 模板引擎
在template里新建一个base.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>base</title>
</head>
<body>
{#html + 模板语言 ————> 模板 模板引擎负责翻译模板语言#}
<h1>BASE</h1>
<hr>
{{ name1 }}
<hr>
{{ name2 }}
<hr>
{{ names }}
<hr>
{{ name3 }}
app里没有传name3 name3没有取到就不显示 也不报错
</body>
</html>
app.py文件里:
from flask import Flask,render_template,request,jsonify,json
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/')
def show_base():
actor1 = '葫芦娃' #现在想把这个name传到html页面 怎么做?
actor2 = '葫芦兄弟'
names = ['葫芦娃','迪迦奥特曼','喜洋洋','小豪']
return render_template('base.html',name1=actor1,name2=actor2,names=names) #渲染模板 这里才是真正的渲染到页面上
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
结果:
可以看到names本来是列表 传出来是字符串 我们想遍历把数据一条一条的取出来
但是类型是字符串 那么我们该怎么办?
需要使用:
遍历 FOR
语句: if for {% for xxx in xxx %} {% endfor %}
app.py文件里新添加一个类对象、一个dict字典格式用于测试:
from flask import Flask,render_template,request,jsonify,json
import settings
app = Flask(__name__)
app.config.from_object(settings)
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return self.name
@app.route('/')
def show_base():
actor1 = '葫芦娃' #现在想把这个name传到html页面 怎么做?
actor2 = '葫芦兄弟'
names = ['葫芦娃','迪迦奥特曼','喜洋洋','小豪']
data = {
'name':'麻婆豆腐',
'author':'张艺兴',
'country':'中国',
'number':10000
}
stu = Student('徐小伟',2)
return render_template('base.html',name1=actor1,name2=actor2,names=names,data=data,student=stu) #渲染模板 这里才是真正的渲染到页面上
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
base.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>base</title>
</head>
<body>
{#html + 模板语言 ————> 模板 模板引擎负责翻译模板语言#}
<h1>BASE</h1>
<hr>
{{ name1 }}
<hr>
{{ name2 }}
<hr>
{{ names }}
<hr>
{#遍历列表的值#}
{% for num in names %}
<p>
{{ num }}
</p>
{% endfor %}
<hr>
{# 取出列表的具体某个值 推荐使用.的方式 #}
{{ names[2] }}
{{ names.2 }}
<hr>
{#遍历字典的值#}
{% for vim in data.values() %}
<p>
{{ vim }}
</p>
{% endfor %}
<hr>
{#遍历字典的键值对#}
{% for k,v in data.items() %}
<p>
{{ k }}-----{{ v }}
</p>
{% endfor %}
<hr>
{# 取字典的某个具体值 #}
{{ data.name }}
{{ data.country }}
<hr>
{#类对象#}
学生的名字是:{{ student }}
学生的年龄是:{{ student.age }}
</body>
</html>
来一个复杂的:把下面的数据放入一个个小格子里?
'bname': 'flask精通',
'price': 50,
'author': 'Echo'
app.py文件里:
from flask import Flask,render_template,request,jsonify,json
import settings
app = Flask(__name__)
app.config.from_object(settings)
datas = (
{
'bname': 'flask精通',
'price': 50,
'author': '汪涵'
},
{
'bname': 'java从入门到放弃',
'price': 88,
'author': '徐小伟'
},
{
'bname': 'Linux菜鸟教程',
'price': 56,
'author': '张富贵'
},
{
'bname': '曼巴自传',
'price': 50,
'author': '科比'
}
)
@app.route('/books/')
def show_books():
return render_template('books.html',allbooks=datas)
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
books.html文件里:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>所有书籍</title>
</head>
<body>
<table border="1" cellpadding="0" width="50%">
{% for book in allbooks %}
<tr>
<td>{{ loop.index }}</td>
{#loop循环 loop.index 获取循环的次数#}
{#loop.index0表示从0开始 loop.revindex表示序号反转#}
<td>{{ book.bname }}</td>
<td>{{ book.price }}</td>
<td>{{ book.author}}</td>
<td>
<a href=""#>删除</a>
<a href="#">更新</a>
</td>
</tr>
{% endfor %}
</table>
<hr>
</body>
</html>
条件判断 IF
实现和百度贴吧下面页码一样的效果 在哪一页 哪一页就没有边框
books.html文件:
!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>IF的使用</title>
<style>
#box a{
color: black;
border: 1px solid gray;
padding: 5px;
text-decoration: none;
margin-right: 3px;
}
</style>
</head>
<body>
{#if else的使用#}
if的使用场景:
{% if school=='echo教育' %}
{{ school }}
{% else %}
匹配不成功
{% endif %}
<hr>
<div id="box" >
<a href="/books/?page=1" {% if page==1 %}style="border: none" {% endif %}>1</a>
<a href="/books/?page=2" {% if page==2 %}style="border: none" {% endif %}>2</a>
<a href="/books/?page=3" {% if page==3 %}style="border: none" {% endif %}>3</a>
<a href="/books/?page=4" {% if page==4 %}style="border: none" {% endif %}>4</a>
<a href="/books/?page=5" {% if page==5 %}style="border: none" {% endif %}>5</a>
</div>
</body>
</html>
app.py文件:
from flask import Flask,render_template,request,jsonify,json
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/books/')
def show_books():
page = request.args.get('page',1)#默认在第一页 1没有边框
print(type(page))
# http: // 127.0.0.1:5000/book/?page = 3
# 传过来的值是字符串 str ‘3’ 需要转化为整型
return render_template('books.html',school='echo教育',page=int(page))
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
6.11模板继承
为什么要用模板继承?
有A、B、C三个页面 但是三个页面的头尾都是一样的
extends
A:头。。。。。。尾
B:头。。。。。。尾
C: 头。。。。。。尾
提取公共部分:
父模板中
子模板继承父模板
步骤:
1. 定义父模板parent.html
在父模板中“挖坑”
{% block 名字%}
{%endblock%}
作为一个父模板: 至少4个坑 title css content js
2. 子继承父模板:
{% extends 'parent.html' %}
在子模板中‘填坑’
{% block 名字%}
填充内容
{%endblock%}
3. 使用模板:
render_template('news.html')
在template里新建一个parent.html文件
百度bootstrap的文档找到css样式里的导航条代码,复制到body里
因为里面有许多类不能用,所以在起步里找CDN加速服务 添加到head里
parent.py:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}父模板{% endblock %}</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">首页 <span class="sr-only">(current)</span></a></li>
<li><a href="#">新闻</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">搜索</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">登陆</a></li>
<li><a href="#">注册</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
</body>
</html>
app.py:
from flask import Flask,render_template,request,jsonify,json
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/news/')
def show_news():
return render_template('parent.html')
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
然后进入127.0.0.1:5000/news/ 我们可以看到一个有导航条的页面
然后在template再新建一个页面 index.html
{#想把父类里的东西都继承过来 怎么拿呢#}
{% extends ‘parent.html’ %}
这个时候 再把 app.py里面的 render_template(‘parent.html’)改为render_template(‘index.html’)
from flask import Flask,render_template,request,jsonify,json
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/news/')
def show_news():
return render_template('index.html')
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
但是网站的标题还是“父模板”
可以在index.html里面更改网页名为“首页”
{#想把父类里的东西都继承过来 怎么拿呢#}
{% extends 'parent.html' %}
{% block title %}
首页
{% endblock %}
再添加个body
{#想把父类里的东西都继承过来 怎么拿呢#}
{% extends 'parent.html' %}
{% block title %}
首页
{% endblock %}
<body>
<h1>这个是首页</h1>
</body>
但是我们刷新网页发现没有显示“这个是首页”
为什么呢?因为我们是继承的父模板 父模板里面我们除了写导航条 没有预留其他body
那么我们去父模板parent.html文件里面挖坑 留下位置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}父模板{% endblock %}</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<nav class="navbar navbar-default">
.......
</nav>
{% block content %}
{% endblock %}
</body>
</html>
然后再更改index.html文件的内容:
{#想把父类里的东西都继承过来 怎么拿呢#}
{% extends 'parent.html' %}
{% block title %}
首页
{% endblock %}
{% block content %}
<body>
<h1>这里是首页</h1>
<input type='button' value='测试'>
</body>
{% endblock %}
再进入网页 127.0.0.1:5000/news/ 我们可以看到显示了"这里是首页"和一个测试按钮
所以如果我们要做父模板 一定要把坑挖好 方便子代来填坑!!!
我们在template里再新建一个news.html文件 同样是继承parent.html文件
{% extends 'parent.html' %}
{% block title %}
新闻
{% endblock %}
{% block content %}
<ul>
<li>学校食堂食物过期</li>
<li>北京明天有风</li>
<li>伟哥高薪就业 年薪百万</li>
<li>范冰冰缴税成功</li>
<li>吴秀波案件反转</li>
</ul>
{% endblock %}
app.py文件里做一些更改:
from flask import Flask,render_template,request,jsonify,json
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/index/')
def show_index():
return render_template('index.html')
@app.route('/news/')
def show_news():
return render_template('news.html')
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0')
进入http://127.0.0.1:5000/index/页面
我们可以看到:
进入http://127.0.0.1:5000/news/页面
我们可以看到:
如果想给index里的input标签加样式 应该怎么加样式?
不能直接加在
{% block content %}
…
{% endblock %}
因为block content是在body里的 scrtipt需要加在head里
所以继续去parent.html的head里挖坑
<!DOCTYPE html>
<html lang="en">
<head>
......
{% block mycss %}
{% endblock %}
</head>
<body>
<nav class="navbar navbar-default">
......
</nav>
{% block content %}
{% endblock %}
</body>
</html>
再给按钮加一个点击脚本事件 这个坑挖在parent.html最后 因为最后的地方把前面都加载完了
加载完了肯定能找到对象
<!DOCTYPE html>
<html lang="en">
<head>
......
{% block mycss %}
{% endblock %}
</head>
<body>
<nav class="navbar navbar-default">
......
</nav>
{% block content %}
{% endblock %}
{% block myjs %}
{% endblock %}
</body>
</html>
在index.html进行修改:
{#想把父类里的东西都继承过来 怎么拿呢#}
{% extends 'parent.html' %}
{% block title %}
首页
{% endblock %}
{% block mycss %}
<style>
#box{
width: 200px;
height: 200px;
background-color: red;
}
</style>
{% endblock %}
{% block myjs %}
<script>
btn = document.getElementById('btn')
btn.onclick = function(){
alert('aaaa——————>')
}
</script>
{% endblock %}
{% block content %}
<div id="box"></div>
<h1>这里是首页</h1>
<input type='button' value='测试' id="btn">
{% endblock %}
进入127.0.0.1:5000/index/ 点击按钮
还可以去给news.html加样式
{% extends 'parent.html' %}
{% block title %}
新闻
{% endblock %}
{% block content %}
<ul>
<li>学校食堂食物过期</li>
<li>北京明天有风</li>
<li>伟哥高薪就业 年薪百万</li>
<li>范冰冰缴税成功</li>
<li>吴秀波案件反转</li>
</ul>
{% endblock %}
{% block mycss %}
<style>
ul{
list-style: none;
}
li{
font-size: 20px;
color: green;
margin-bottom: 10px;
}
</style>
{% endblock %}
可以看到每条新闻前面的点没有了:
继承的总结
步骤:
定义父模板parent.html
在父模板中“挖坑”
{% block 名字%}
{%endblock%}
作为一个父模板: 至少4个坑 title css content js
以后工作的话 对接前端的人 看一下他写的网页的结构
比如他的网页的结构式左中右三个大内容
就可以在body里面挖三个坑
{% block content1 %}
{% endblock %}
{% block content2 %}
{% endblock %}
{% block content3 %}
{% endblock %}
-
子继承父模板:
{% extends ‘parent.html’ %}在子模板中‘填坑’
{% block 名字%}
填充内容{%endblock%}
-
使用模板:
render_template(‘news.html’)
6.12项目结构的搭建
查看笔记《flask项目的搭建》
6.13数据添加
用户注册
template模板中的register.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户注册</title>
</head>
<body>
<h3>用户注册</h3>
<form action="{{ url_for('user.register') }}" method="post">
<p><input type="text" name="username" placeholder="输入用户名"></p>
<p><input type="password" name="password" placeholder="输入密码"></p>
<p><input type="password" name="repassword" placeholder="输入确认密码"></p>
<p><input type="submit" value="注册"></p>
</form>
<p style="color:red">{{ msg }}</p>
</body>
</html>
views视图中的python代码:
在views包中的user_view.py文件中:
import hashlib
from flask import Blueprint, request, render_template, redirect, url_for
from sqlalchemy import or_, and_, func
from app.models.user_model import User
from exts import db
user_bp = Blueprint('user',__name__,url_prefix='/user')
@user_bp.route('/register',methods=['GET,'POST'])
def register():
if request.method=='GET':
return render_template('register.html')
else:
# 获取输入
username = request.form.get('username')
password = request.form.get('password')
repassword = request.form.get('repassword')
if password != repassword:
return render_template('register.html', msg='两次密码不一致')
# 数据库添加
# 创建对象User
user = User()
user.username = username
user.password = hashlib.sha256(password.encode('utf-8')).hexdigest() # 明文 123456
# 添加到db上
db.session.add(user)
# 提交事务
db.session.commit()
return '注册成功'
用户登录
template模板中的login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<h3>用户登录</h3>
<form action="{{ url_for('user.login') }}" method="post">
<p><input type="text" name="username" placeholder="输入用户名"></p>
<p><input type="password" name="password" placeholder="输入密码"></p>
<p><input type="submit" value="登录"></p>
</form>
<p style="color:red">{{ msg }}</p>
</body>
</html>
views视图中的python代码:
在views包中的user_view.py文件中添加路由:
@user_bp.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
# 获取提交的数据
username = request.form.get('username')
password = request.form.get('password')
# 去数据库中查询
user = User.query.filter_by(username=username).first()
if user:
# print(user.password)
hash_pwd = hashlib.sha256(password.encode('utf-8')).hexdigest()
if user.password == hash_pwd:
print(url_for('user.show')) # /user/show
return redirect(url_for('user.show'))
# return render_template('show.html')
return render_template('login.html', msg='用户名或者密码有误!')
展示用户信息show
template模板中的show.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户信息展示</title>
</head>
<body>
<h4>用户信息如下:</h4>
<table border="1" cellspacing="0" width="40%">
<tr>
<th>序号</th>
<th>用户名</th>
<th>注册时间</th>
<th>是否删除</th>
<th>操作</th>
</tr>
{% for user in users %}
<tr>
<td>{{ loop.index }}</td>
<td><a href="{{ url_for('user.articles') }}?id={{ user.id }}">{{ user.username }}</a></td>
<td>{{ user.add_time }}</td>
<td>{{ user.isdelete }}</td>
<td>
{# <a href="{{ url_for('user.delete',id=user.id) }}">删除</a>#}
{# {{ url_for('user.delete') }} ---> /user/delete?id=2 #}
<a href="{{ url_for('user.delete') }}?id={{ user.id }}">删除</a>
<a href="#">更新</a>
</td>
</tr>
{% endfor %}
</table>
</body>
</html>
注:url_for:反向解析
views视图中的python代码:
@user_bp.route('/show')
def show():
# 给管理员展示所有的用户信息
# users = User.query.filter_by(isdelete=False).all() # 查询表中所有的数据 select * from user where isdelete=0
users = User.query.all()
print(users)
return render_template('show.html', users=users)
数据删除
views视图中的python代码:
@user_bp.route('/delete')
def delete():
# 逻辑删除,修改isdelete的值
id = request.args.get('id')
user = User.query.filter_by(id=id).first()
user.isdelete = True
db.session.commit()
# db.session.delete(user) db.session.commit()
return redirect(url_for('user.show'))
注:物理删除
1.对象 2. db.session.delete(对象) 3. db.session.commit()
数据查询
查询:
User.query.filter(User.username.contains(‘admin123’)).all()
User.query.filter(User.username.contains(‘admin123’)).first()
User.query.filter(User.username.contains(‘admin123’)).count()
User.query.filter(User.username.contains(‘admin123’)).order_by(-User.age)
…User.query.all() ---->切片[1:5]
User.query.get(id)
and_ or_
template模板中的search.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>展示查询结果</title>
</head>
<body>
{#{{ user.username }} ---- {{ user.add_time }}#}
{{ users }}
{% if users %}
{% for user in users %}
<p>{{ user.username }}-----{{ user.age }}</p>
{% endfor %}
views视图中的python代码:
@user_bp.route('/search/<int:id>')
def search(id):
# User.query.all() 查询所有信息
# get(value) ---> 表示 根据主键获取用户对象
# user = User.query.get(id) # user = User.query.filter_by(id=1).first()
# user = User.query.filter_by(username='admin123',
# isdelete=True).first() # select * from user where username='admin123' and isdelete=False
# print(user) # 返回值None
# users = User.query.filter(and_(User.age>18, User.username.contains('a'))).all()
# users = User.query.filter(User.age.between(18,25)).all()
# or_ and_ 只有字符串字段才可以使用startswith endswith contians like between
count = User.query.filter(User.age >= 18).count()
print('===========>', count)
result = db.session.query(func.min(User.age).label('avg_age')).one()
print("====>%s" % result)
# users = User.query.filter(User.age>=18).order_by(-User.age).limit(3).all()
users = User.query.filter(User.age >= 18).order_by(-User.age).all()[1:3] # 使用切片
return render_template('search.html', users=users)
6.14redirect重定向
return redirect(url_for(‘user.show’))----------->重定向
return render_template(‘show.html’)---------->服务器内部获取模板信息返回
区别:
1.重定向 地址栏发生改变,第二种方式是没有改变的
2.重定向 携带状态吗302到达客户端浏览器,浏览器获取状态码302之后修改地址栏的url为:location的值 /user/show,http://127.0.0.1:5000/user/show ,浏览器发出修改后的url的请求。
6.15 一对多关系
lass User(db.Model):
id=xxx
name=xxx
# relationship #backref反向查找
articles = db.relationship('Article',backref='user')
class Article(db.Model):
id=xxx
title=xxx
添加外键
user_id = db.Column(db.Integer,db.ForeignKey('user.id'))
user.articles
article.user
6.16多对多
User -- goods
student -- course
user --- movie
第一: user模型和goods模型都是独立,建立两者关系
第二: 创建两张表的关系表:就是建立关系(通过外键)
class User_goods(BaseModel):
__tablename__ = 'user_goods'
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
goods_id = db.Column(db.Integer, db.ForeignKey('goods.id'))
另外的方式:
tags = db.Table('tags',
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True),
db.Column('page_id', db.Integer, db.ForeignKey('page.id'), primary_key=True)
)
第三: goods = db.relationship('Goods',backref='users',secondary='关系表的表名')
6.17Cookie和Session
cookie
會話機制:http
无状态协议。
Cookie:
设置Cookie:
通过response对象完成设置:
得到response对象:
make_response()
Response() text/html
redirect()
jsonify() application/json
response.set_cookie(key,value) ----->设置到客户端的浏览器key=value
获取cookie:
通过request对象: 只要发出请求,就会默认携带cookie 127.0.0.1
request.cookies.get(key) ----> value
删除cookie:
通过response对象:
response.delete_cookie(key) -----> 删除key对应的键值对
设置cookie的过期时间
expires = datetime.now()+timedelta(days=30) -----> 时间差
res.set_cookie('flag', 'hello',expires=expires)
res.set_cookie('kkk',18,max_age=60) -----> max_age单位是秒
session
Session:
必须在配置文件中设置secret_key
SECRET_KEY='dhfjh244djsqopfv5790874878jchfjh'
设置session:
session['uname']='admin123'
在配置文件中:
SECRET_KEY = 'dhfjh244djsqopfv5790874878jchfjh'
PERMANENT_SESSION_LIFETIME = timedelta(hours=1)
获取session:
session.get('uname') ---->value
退出:
session.clear()
6.18钩子函数
flask 钩子函数: django 中表示 中间件
钩子函数:
app.route('/')
def index():
pass
@app.before_first_request()
def 函数():
完成
@app.before_request
def 函数():
pass
@app.after_request
def 函数():
pass
@app.teardown_request
def 函数():
pass
6.19文件上传
1.设置表单内容:
<form action="" method="post" enctype="multipart/form-data"> ----》 必须添加enctype属性为:multipart/form-data
<p><input type="hidden" name="id" value="{{ user.id }}"></p>
<p><input type="text" name="username" value="{{ user.username }}"></p>
<p><input type="text" name="age" value="{{ user.age }}"></p>
{% if not user.icon %}
<p><input type="file" name="icon"></p>
{% else %}
<p><input type="text" name="icon" value="{{ user.icon }}"></p>
{% endif %}
<p><input type="submit" value="保存设置"></p>
</form>
--------------------------------------------------------------
2. 后台获取内容:
# 文件上传的
icon_obj = request.files.get('icon') # FileStorage:
可以通过该对象获取文件名: icon_obj.filename
也可以通过该对象保存文件: icon_obj.save(保存文件的路径)
文件路径的设置:在settings中设置
BASE_DIR = os.path.dirname(__file__)
UPLOAD_DIR = os.path.join(BASE_DIR, 'app/static/upload')
保存文件:
uploadpath = os.path.join(UPLOAD_DIR, icon_obj.filename)
icon_obj.save(uploadpath)
--------------------------------------------------------------
3. 获取文件:
可以通过保存的路径截取:
username = session.get('uname')
if username:
user = User.query.filter(User.username == username).first()
if user.icon:
filename = user.icon.rsplit('\\', 1)[1]
return render_template('index.html',filename=filename)
6.20Redis缓存
redis缓存:
1. pip install redis
pip install flask-caching
2. exts ---> __init__.py
cache = Cache()
3. 在app初始化:
cache.init_app(app=app,config=settings.REDIS_CONFIG)
字典类型配置settings:
REDIS_CONFIG = {
'CACHE_TYPE': 'redis',
'CACHE_REDIS_HOST': '127.0.0.1',
'CACHE_REDIS_PORT': 6379,
'CACHE_REDIS_DB': 1
}
4. 使用cache对象:添加缓存
A. cache.set(key,value,timeout=60)
cache.get(key) --->value
B. 缓存视图函数:
@app.route("/")
@cache.cached(timeout=50)
def index():
return render_template('index.html')
二:加密方式:
1. 使用内置的方式加密
generate_password_hash(password) ---》 加密后的结果
check_password_hash(加密过得,明文的密码)
2. 使用hashlib中的加密算法加密:
hash_obj = hashlib.sha256(password.encode('utf-8'))
通过加密对象,获取十六进制
hash_obj.hexdigest()
三、ajax的使用:
$(function () {
$('#username').blur(function () {
$input = $(this);
var username = $input.val();
var $span = $('#tips');
{# get请求的ajax $.ajax() $.post() $.get() $.getJSON() #}
$.getJSON('/user/checkuname', {username: username}, function (data) {
if (data.status == 200) {
if (data.msg == 'ok') {
$span.html('此用户名可用')
} else {
$span.html(data.msg)
}
} else {
alert('用户名检查失败')
}
})
})
})
mkvirtualenv django1901
pip install django==2.0
pip install pymysql