0217 flask框架介绍
Flask框架
1. flask
3大web框架
1 django
django是一个同步框架,orm,模板都是自己写的,如果你要快速开发,一个比较大的项目就用django。相当于御姐,什么都有,你不需要做任何调教,你想要都有,你想不到它也有。
2 flask
flask也是一个同步框架,orm不是自己写的,jinja2模板,flask小项目,能不能做大项目?能,它相当于一个纯情妹子,什么都不懂,但是你教它以后,它就什么都懂了,它需要你自己去做开发。
3 torando
torando它是异步框架,都没有。
简介
Flask 属于微框架(micro-framework)这一类别,我们可以使用Python语言快速实现一个网站或Web服务。微架构通常是很小的不依赖于外部库的框架。这既有优点也有缺点,优点是框架很轻量,更新时依赖少,缺点是你不得不自己做更多的工作,或通过添加插件增加自己的依赖列表。flask默认的模板渲染引擎是janjia2
flask作为一个轻量级框架,它里面有好多扩展包需要下载,比较麻烦,而且有的时候flask需要在虚拟环境下运行,但是他的优点还是有滴 ,只要是用过Django的人,都会觉得flask是真的 '轻'
Flask自由、灵活,可扩展性强,能结合最流行最强大的Python库
入门简单,即便没有多少web开发经验,也能很快做出网站
非常适用于小型网站
非常适用于开发web服务的API
开发大型网站无压力,但代码架构需要自己设计,开发成本取决于开发者的能力和经验
各方面性能均等于或优于Django
Django自带的或第三方的好评如潮的功能,Flask上总会找到与之类似第三方库
Flask灵活开发,Python高手基本都会喜欢Flask,但对Django却可能褒贬不一
Flask与关系型数据库的配合使用不弱于Django,而其与NoSQL数据库的配合远远优于Django
flask里面的技术点有很多,比如cookie ,session ,过滤器 ,四个钩子,werkzeug和jinja2 ,蓝图.等等.
先来说说这四个钩子,很多人会问:钩子?什么钩子,肯定一脸懵逼,对于钩子我的理解是: 可以把flask四种钩子看作是修饰器,我们在后端可以进行调用做相关的操作.使用钩子函数时,我们需要借助flask的全局变量g.g作为中间变量,在钩子函数和视图函数中间传递数据.我们先引入全局变量g
安装
pip install flask
创建项目
Flask不同于Django,不会提供任何自动的操作,所以需要手动创建项目目录,需要手动创建启动项目的管理文件
例如,创建项目目录flaskdemo,在目录中创建manage.py。在pycharm中打开项目并指定上面创建的虚拟环境
1.导入flask类
form flask import Flask
2.实例化一个flask对象
app = Flask(__name__)
# __name__是模块的名称或者包的名称
# 作用: 根据这个参数确定flask应用的路径, 从而快速查找模板和html文件的默认路径;也可以说这里的app就是一个程序实例,客户端(一般是浏览器)将请求发送给服务端Web服务器,Web服务器再把请求发给Flask实例。
3.基本路由
@app.route('/')
def index():
return 'Hello World'
# @app.route(’/’): 告诉Flask哪个url才能出发对应的函数,又称为路由;对应定义了一个视图函数,也就是返回给用户浏览器显示的内容; 即路由所对应的程序称为视图函数(view function),即上面的index()函数
4.运行flask应用
if __name__ == '__main__':
app.run()
# app.run()运行应用,启动服务器,可以指定ip和端口‘0.0.0.0’ 所有的IP都可以访问到 例如:app.run('0.0.0.0', 9000)
配置类
#加载项目配置
#配置类
class Config(object)
DEBUG = True
SECRET_KEY = "aaabbbbbddddeeeeb"
app.config.from_object(Config)
#指定服务器IP和端口
app.run(host="0.0.0.0",port=5000,debug = True)
2. flask的四剑客
1.直接返回字符串
直接return字符串显示数据
2.render_template
返回html页面,类似于Django的render
要创建一个templates文件夹,把所有的HTML文件都放在其中
初始化app.py设置文件中参数templates_folder参数指定了文件夹的目录
3.redirect
跳转页面
与Django中的redirect一致
4.jsonify
就是将可以序列化的数据转化为json返回
代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Fwzzz'
from flask import Flask,render_template,redirect,jsonify
app = Flask(__name__)
# 字符串
@app.route('/')
def index():
return 'ok'
#HTML页面
@app.route('/index')
def index1():
return render_template('index.html')
#跳转
@app.route('/login')
def index2():
return redirect('/')
#json数据
@app.route('/json')
def json11():
data = {'name':'tank','love':'piao'}
return jsonify(data)
if __name__ == '__main__':
app.run()
3. flask的配置文件
方式1 (app.属性
)
只能配置两项(在文件中书写)
app.debug = True
app.secert_key = 'abc'
方式2 (app.config字典
)
全大写属性
app.config['DEBUG'] = True
方式3 (以文件的形式进行配置
)app.config.from_pyfile()
另外新建一个文件,文件中改变
app.config.from_pyfile('文件路径.py')
设置新建文件setting:
DEBUG = False
方式4 (以类的形式 推荐使用
)app.config.from_object()
新建文件,文件中使用类的方式进行设置,可以方便调试,Flask配置的随时切换
app.config.from_object('类的文件.类名.py')
设置文件:
class Config:
DEBUG = False
# 产品配置
class ProductConfig(Config):
pass
# 测试文件使用
class TestConfig(Config):
pass
4. flask的路由
路由的本质
#指定访问路径为demo1
@app.route('/demo1')
def demo1():
return 'demo1'
本质就是
@app.route('/detail/<int:nid>',methods=['GET'],endpoint='detail')
"""
1. decorator = app.route('/',methods=['GET','POST'],endpoint='n1')
def route(self, rule, **options):
# app对象
# rule= /
# options = {methods=['GET','POST'],endpoint='n1'}
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
2. @decorator
decorator(index)
"""
#同理
def login():
return '登录'
app.add_url_rule('/login', 'n2', login, methods=['GET',"POST"])
#与django路由类似
#django与flask路由:flask路由基于装饰器,本质是基于:add_url_rule
#add_url_rule 源码中,endpoint如果为空,endpoint = _endpoint_from_view_func(view_func),最终取view_func.__name__(函数名)
url构建与反向解析(url_for
)
from flask import Flask, request, url_for
app=Flask(__name__)
@app.route('/welcome/<string:user>')
def welcome(user):
return user +'用户,欢迎光临'
@app.route('/path/')
def path():
print(request.headers)
#查看默认url地址的请求方法
print(request.method)
#url_for:根据函数名,反向生成url地址
print('用户正在访问url地址:%s' %(url_for(endpoint='welcome',user='Merry')))
return '用户正在访问url地址:%s' %(url_for(endpoint='welcome',user='Merry'))
app.run()
通过 request类,可以得到其头部信息或者访问模式,是 get 或者 post 等
app.add_url_rule
参数
@app.route和app.add_url_rule参数:
rule
URL规则
view_func
视图函数名称
defaults = None
默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}为函数提供参数
endpoint = None,
名称,用于反向生成URL,即: url_for('名称')
methods = None,
允许的请求方式,如:["GET", "POST"]
strict_slashes = None
#对URL最后的 / 符号是否严格要求
redirect_to = None,
#重定向到指定地址
代码
'''
@app.route('/index', strict_slashes=False)
#访问http://www.xx.com/index/ 或http://www.xx.com/index均可
@app.route('/index', strict_slashes=True)
#仅访问http://www.xx.com/index
'''
#重定向到指定地址
redirect_to = None,
'''
@app.route('/index/<int:nid>', redirect_to='/home/<nid>')
'''
#子域名访问
subdomain = None,
'''
#C:\Windows\System32\drivers\etc\hosts
127.0.0.1 www.liuqingzheng.com
127.0.0.1 admin.liuqingzheng.com
127.0.0.1 buy.liuqingzheng.com
from flask import Flask, views, url_for
app = Flask(import_name=__name__)
app.config['SERVER_NAME'] = 'liuqingzheng.com:5000'
@app.route("/", subdomain="admin")
def static_index():
"""Flask supports static subdomains
This is available at static.your-domain.tld"""
return "static.your-domain.tld"
#可以传入任意的字符串,如传入的字符串为aa,显示为 aa.liuqingzheng.com
@app.route("/dynamic", subdomain="<username>")
def username_index(username):
"""Dynamic subdomains are also supported
Try going to user1.your-domain.tld/dynamic"""
return username + ".your-domain.tld"
if __name__ == '__main__':
app.run()
访问:
http://www.liuqingzheng.com:5000/dynamic
http://admin.liuqingzheng.com:5000/dynamic
http://buy.liuqingzheng.com:5000/dynamic
'''
路由传参(两种方式)
不限制类型
# 路由传递参数[没有限定类型]
@app.route('/user/<user_id>')
def user_info(user_id):
return 'hello %s' % user_id
限制类型的有名分组
# 路由传递参数[限定数据类型]
@app.route('/user/<int:user_id>')
def user_info(user_id):
return 'hello %d' % user_id
# 默认转换器
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
正则匹配路由
在 web 开发中,可能会出现限制用户访问规则的场景,那么这个时候就需要用到正则匹配,根据自己的规则去限定请求参数再进行访问
具体实现步骤为:
- 导入转换器基类:在 Flask 中,所有的路由的匹配规则都是使用转换器对象进行记录
- 自定义转换器:自定义类继承于转换器基类
- 添加转换器到默认的转换器字典中
- 使用自定义转换器实现自定义匹配规则
代码
> 导入转换器基类
from werkzeug.routing import BaseConverter
> 自定义转换器
1 # 自定义正则转换器
2 from werkzeug.routing import BaseConverter
3 class RegexConverter(BaseConverter):
4 def __init__(self,url_map,*args):
5 super(RegexConverter, self).__init__(url_map)
6 # 正则参数
7 self.regex = args[0]
> 添加转换器到默认的转换器字典中,并指定转换器使用时名字为: re
1 # 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: re
2 app.url_map.converters['re'] = RegexConverter
> 使用转换器去实现自定义匹配规则
>> 当前此处定义的规则是 :手机号
1 # 正则匹配路由
2 @app.route("/login/<re('1\d{10}'):mobile>")
3 def login(mobile):
4 return mobile
系统转换器
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'path': PathConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
系统自带的转换器具体使用方式在每种转换器的注释代码中有写,请留意每种转换器初始化的参数。
from flask import Flask
# 新增一个配置文件,在配置文件中设置配置信息
from config import Config
from flask import request
# from collections import OrderedDict # 有序字典
app = Flask(__name__)
# 调用app.config加载配置
app.config.from_object(Config)
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter):
def __init__(self,url_map,*args):
super(RegexConverter, self).__init__(url_map)
# 正则参数
self.regex = args[0]
# converter["路由转换器名称"] = 实现路由转换功能的自定义类
app.url_map.converters['re'] = RegexConverter
# 绑定路由
@app.route("/")
def index():
return "hello flask"
# 默认情况下,路由使用的就是关键字参数,也叫"命名路由"
# router(路由地址, http请求方式 )
@app.route("/list/<int:page>/<string:content>",methods=["GET","POST"])
def mylist(content,page):
return "第%s页<br>内容:%s" % (page,content)
# 正则匹配路由
@app.route("/login/<re('1\d{10}'):mobile>")
def login(mobile):
return mobile
5. flask的CBV
如果继承的是views.View必须重写def dispatch_request(self)
from flask import Flask,views,url_for
app = Flask(__name__)
#如果继承的是views.View必须实现 def dispatch_request(self):
class IndexView(views.View):
methods = ["POST","GET"]
#decorators= ["装饰器的函数对象",]
def dispatch_request(self):
return "index"
#app.add_url_rule("/index",view_func=view)(View中as_view函数中嵌套的view)
app.add_url_rule("/index",view_func=IndexView.as_view(name="index"))
# 固定写法: app.add_url_rule('路由',view_func=类.as_view(name='反向解析用'))
继承views.MethodView
直接继承了views.MethodView
class LoginView(views.MethodView):
def post(self):
return "post"
def get(self):
print(url_for("123"))
return "get"
app.add_url_rule("/login",view_func=LoginView.as_view(name="login"))
if __name__ == '__main__':
app.run()
6.模板渲染
直接在render_template返回时添加`变量名=值`,前段页面进行接收
return render_template("index1.html",num=a,user_dic = list_info ,htm=html,ff=ff)
代码
from flask import Flask,render_template,Markup
app = Flask(__name__)
# 定义一个函数,可以返回给前端
def ff(arg,b):
return Markup(arg+b)
@app.route("/")
def index():
a = 123
list_info ={
1:{"name":"tank","long":160,"颜值":50},
2:{"name":"jason","long":170,"颜值":60},
3:{"name":"饼","long":190,"颜值":80}
}
html ="<h1> jason is sb</h1>"
return render_template("index1.html",num=a,user_dic = list_info ,htm=html,ff=ff)
# 这里将函数传到前段页面以及参数html,可以在前段页面进行处理
if __name__ == '__main__':
app.run()
index1.html
渲染变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
{% for k,v in user_dict.items() %}
<tr>
<td>{{k}}</td>
<td>{{v.name}}</td>
<td>{{v['name']}}</td>
<td>{{v.get('name')}}</td>
<td><a href="/detail/{{k}}">查看详细</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
变量的循环
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
{% for k,v in user_dict.items() %}
<tr>
<td>{{k}}</td>
<td>{{v.name}}</td>
<td>{{v['name']}}</td>
<td>{{v.get('name')}}</td>
<td><a href="/detail/{{k}}">查看详细</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
逻辑判断
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{% endif %}
</table>
</body>
</html>
传递函数(可以加括号)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ff('六五')}}
{{ff('六五')|safe}}
</body>
</html>
传递html文字
使用Markup 等价于Django的mark_safe
extends,include一模一样
from flask import Flask,render_template,Markup,jsonify,make_response
app = Flask(__name__)
def func1(arg):
return Markup("<input type='text' value='%s' />" %(arg,))
@app.route('/')
def index():
return render_template('index.html',ff = func1)
if __name__ == '__main__':
app.run()
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ff('六五')}}
{{ff('六五')|safe}}
</body>
</html>
7.请求与响应
请求参数
# 请求相关信息
# request.method 提交的方法
# request.args get请求提及的数据
# request.form post请求提交的数据
# request.values post和get提交的数据总和
# request.cookies 客户端所带的cookie
# request.headers 请求头
# request.path 不带域名,请求路径
# request.full_path 不带域名,带参数的请求路径
# request.script_root
# request.url 带域名带参数的请求路径
# request.base_url 带域名请求路径
# request.url_root 域名
# request.host_url 域名
# request.host 127.0.0.1:500
# request.files
# obj = request.files['the_file_name']
# obj.save('/var/www/uploads/' + secure_filename(f.filename))
响应参数
# 响应相关信息
# return "字符串"
# return render_template('html模板路径',**{})
# return redirect('/index.html')
#return jsonify({'k1':'v1'})
# response = make_response(render_template('index.html'))
# response是flask.wrappers.Response类型
# response.delete_cookie('key')
# response.set_cookie('key', 'value')
# response.headers['X-Something'] = 'A value'
# return response
return "内容"
代码
from flask import Flask
from flask import request
from flask import render_template
from flask import redirect
from flask import make_response
app = Flask(__name__)
@app.route('/login.html', methods=['GET', "POST"])
def login():
# 请求相关信息
# request.method 提交的方法
# request.args get请求提及的数据
# request.form post请求提交的数据
# request.values post和get提交的数据总和
# request.cookies 客户端所带的cookie
# request.headers 请求头
# request.path 不带域名,请求路径
# request.full_path 不带域名,带参数的请求路径
# request.script_root
# request.url 带域名带参数的请求路径
# request.base_url 带域名请求路径
# request.url_root 域名
# request.host_url 域名
# request.host 127.0.0.1:500
# request.files
# obj = request.files['the_file_name']
# obj.save('/var/www/uploads/' + secure_filename(f.filename))
# 响应相关信息
# return "字符串"
# return render_template('html模板路径',**{})
# return redirect('/index.html')
#return jsonify({'k1':'v1'})
# response = make_response(render_template('index.html'))
# response是flask.wrappers.Response类型
# response.delete_cookie('key')
# response.set_cookie('key', 'value')
# response.headers['X-Something'] = 'A value'
# return response
return "内容"
if __name__ == '__main__':
app.run()
8. cookie
cookie:存放在客户端的键值对
session:存放在客户端的键值对
token:存放在客户端,通过算法来校验
设置cookie
make_response实例化得到response对象然后set_cookie
设置cookie需要通过flask的Response响应对象来进行设置,由flask内部提供了一个make_response函数给我们可以快速创建响应对象
from flask imoprt Flask,make_response
@app.route('/set_cookie')
def set_cookie():
resp = make_response('this is to set cookie')
resp.set_cookie('username', 'xiaoming', max_age=3600)
# max_age 为cookie有效期,单位为秒
return resp
获取cookie
from flask import Flask,request
@app.route('/get_cookie')
def resp_cookie():
resp = request.cookies.get('username')
return resp
删除cookie
response.delete_cookie("name")
9. session
对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息
在服务器端进行状态保持的方案就是Session
注意: Session依赖于Cookie,而且flask中使用session,需要配置SECRET_KEY选项,否则报错.
app.secret_key="asdas" # 值随便(保证session不可预测)
代码
from flask import Flask,session
app = Flask(__name__)
app.secret_key = "aihsdasnd"
app.config['SESSION_COOKIE_NAME']="sbd"
@app.route("/")
def index():
session['name'] = "jason"
return "ok"
@app.route("/login")
def login():
print(session["name"])
return "ojbk"
if __name__ == '__main__':
app.run()
特点
#在django中发什么三件事
1,生成一个随机的字符串
2 往数据库存
3 写入cookie返回浏览器
#在flask中他没有数据库,但session是怎样实现的?
# 生成一个密钥写入这个cookie,然后下次请求的时候,通过这个cookie解密,然后赋值给session
#我们通过app.session_interface来查看
#flask的sessoin存的步骤
# 第一步将session的这个字典做加密得到val,
# 第二步将 配置文件中SESSION_COOKIE_NAME作为key,
# 第三步,设置cookies,以上述的key,val,做键值
#flask的session取的步骤
# 第一步,获取cookies中键为SESSION_COOKIE_NAME的值,
# 第二步: 将第一步获取的值做解密操作得到真的val值
设置session
设置:session['username'] = 'xxx'
from flask import session
@app.route('/set_session')
def set_session():
session['username'] = 'xiaoming'
return 'ok!'
获取session
@app.route('/get_session')
def get_session():
return session.get('session的文件名')
# 也可以直接使用session['session名']获取
删除session
删除:session.pop('username', None)
源码流程
key, 键
value='', 值
max_age=None, 超时时间 cookie需要延续的时间(以秒为单位)如果参数是\ None`` ,这个cookie会延续到浏览器关闭为止
expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)
path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问,浏览器只会把cookie回传给带有该路径的页面,这样可以避免将cookie传给站点中的其他的应用。
domain=None, Cookie生效的域名 你可用这个参数来构造一个跨站cookie。如, domain=".example.com"所构造的cookie对下面这些站点都是可读的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果该参数设置为 None ,cookie只能由设置它的站点读取
secure=False, 浏览器将通过HTTPS来回传cookie
httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
---------------------------------------------------------
session源码的执行流程
-save_seesion
-响应的时候,把session中的值加密序列化放大到了cookie中,返回到浏览器中
-open_session
-请求来了,从cookie中取出值,反解,生成session对象,以后再视图函数中直接用sessoin就可以了。
10 闪现(message)
使用
-设置: flash('aaa')
flash('超时错误!',category='x1') # category对设置的信息分类
-取值:get_flashed_message()
data=get_flashed_messages(category_filter='x1')# 根据信息分类来获取值
特点
1 没有设置,取是不会报错的,返回一个空列表
2 设置了就可以在任何一个视图函数中去取
3 取了一次就没有了。(但是在同一次请求中可以取多次)
实例
-假设在a页面操作出错,跳转到b页面,在b页面显示a页面的错误信息
from flask import Flask,flash,get_flashed_messages,request,redirect
app = Flask(__name__)
app.secret_key = 'asdfasdf'
@app.route('/index')
def index():
# 从某个地方获取设置过的所有值,并清除。
val = request.args.get('v')
if val == 'oldboy':
return 'Hello World!'
flash('超时错误',category="x1")
return "ssdsdsdfsd"
# return redirect('/error')
@app.route('/error')
def error():
"""
展示错误信息
:return:
如果get_flashed_messages(with_category=True)
"""
data = get_flashed_messages(category_filter=['x1'])
if data:
msg = data[0]
else:
msg = "..."
return "错误信息:%s" %(msg,)
if __name__ == '__main__':
app.run()
一:闪现flash基本用法
# -*- coding: utf-8 -*-
# @Author : Felix Wang
# @time : 2018/7/5 9:34
from flask import Flask, flash, get_flashed_messages
app = Flask(__name__)
app.debug = True
app.secret_key = 'dddddddd'
# 闪现flash基本用法,本质是通过session来实现的
@app.route('/get')
def get():
# 从某个地方获取设置过的所有值,并清除
data = get_flashed_messages()
print(data)
return 'hello world'
@app.route('/set')
def set():
# 向某个地方设置一个值
flash('哈哈哈')
return 'hello world'
if __name__ == '__main__':
app.run()
二:闪现实例-显示错误信息
# -*- coding: utf-8 -*-
# @Author : Felix Wang
# @time : 2018/7/5 9:34
from flask import Flask, flash, get_flashed_messages,request,redirect
app = Flask(__name__)
app.debug = True
app.secret_key = 'dddddddd'
# 闪现基于session来实现的
# 应用:对临时数据操作;如:显示错误信息
@app.route('/index')
def index():
val=request.args.get('v')
if val=='a':
return 'Hello World!'
flash('超时错误!',category='x1') # category对设置的信息分类
return redirect('/error')
@app.route('/error')
def error():
data=get_flashed_messages(category_filter='x1')# 根据信息分类来获取值
if data:
msg=data[0]
else:
msg=''
return '错误信息,{}'.format(msg)
if __name__ == '__main__':
app.run()