Flask
django和flask的区别.......
django是个大而全的框架,flask是一个轻量级的框架。
django内部为我们提供了非常多的组件:orm / session / cookie / admin / form / modelform / 路由 / 视图 / 模板 / 中间件 / 分页 / auth / contenttype / 缓存 / 信号 / 多数据库连接
flask框架本身没有太多的功能:路由/视图/模板(jinja2)/session/中间件 ,第三方组件非常齐全。
注意:django的请求处理是逐一封装和传递; flask的请求是利用上下文管理来实现的。
安装flask
pip install flask
实现最简单的hello world
from flask import Flask,request,render_template,redirect
app = Flask(__name__) # 这里以接受包或者模块为参数,一般传递__name__ 这里定义了变量app赋值为Flask()
@app.route('/index') # 这里相当于定义了一个路由,这个url和下面的函数存在了一个对应关系,映射关系
def index():
return 'hello world'
if __name__ == '__main__':
app.run()
上段代码做了什么?
1.首先,导入了 Flask 类。这个类的实例将会是我们的 WSGI 应用程序。
2.接下来,创建一个该类的实例,第一个参数是应用模块或者包的名称。 如果你使用单一的模块(如本例),你应该使用 __name__ ,因为模块的名称将会因其作为单独应用启动还是作为模块导入而有不同( 也即是 '__main__' 或实际的导入名)。这是必须的,这样 Flask 才知道到哪去找模板、静态文件等等。详情见 Flask的文档。
3.然后,我用 route() 装饰器告诉 Flask 什么样的URL 能触发我们的函数。
4.这个函数的名字也在生成 URL 时被特定的函数采用,这个函数返回想要显示在用户浏览器中的信息。
5.最后用 run() 函数来让应用运行在本地服务器上。 其中 if __name__ =='__main__': 确保服务器只会在该脚本被 Python 解释器直接执行的时候才会运行,而不是作为模块导入的时候。
写flask步骤
from flask import Flask,request,render_template,redirect,jsonify
app = Flask(__name__) #创建flask对象,模板的默认存放在templates里,通过参数template_folder='模板文件夹名'
@app.route('/index') #路由和视图放一起,路由和函数存在映射关系
def index():
return 'hello world'
# return jsonify({'xx':'xx'}) json格式
if __name__ == '__main__':
app.run() # 在内部调用werkzeug,让程序执行,用户请求一旦到来,就执行路由和视图
实现用户登录
from flask import Flask,request,render_template,redirect,url_for(url别名)
app = Flask(__name__)
@app.route('/login',methods=['GET','POST'],endpoint='login')
#请求方式,可以使GET,POST,当在重定向就可以 redirect(url_for('login'))
def login():
if request.method == 'GET':
pk = request.args.get('pk') #GET 获取url的值
return render_template('login.html')
user = request.form.get('user') #POST 获取表单提交过来的额数据
pwd = request.form.get('pwd')
if user == 'tj' and pwd == 'tj':
return redirect('/index')
else:
error = '用户名或密码错误'
return render_template('login.html',error=error)
return render_template('login.html',**{'error':error})
@article_app.route('/article/del/<int:pk>',methods=['GET','POST'])
#<pk> pk默认字符串形式,可以通过int转换,类似于django的通过正则拼接的路径
#前端中url <a href='/del/{{pk}}'>删除</a>
def remove(pk):
# pk = request.args.get('pk')
sql('delete from article where id=%s'%pk)
return redirect('/article/list')
if __name__ == '__main__':
app.run()
flask蓝图(blueprint)
什么是蓝图?
一个蓝图定义了可用于单个应用的视图,模板,静态文件等等的集合,可以简单理解为路由分发
构建蓝图
目录结构:
login_CURD
login_CURD
static #存放一些配置文件
templates #放HTML
views
视图1
视图2
...
__init__.py
manage.py
auth_app
from flask import Blueprint,render_template,request,redirect,session
from bms_flask.static.sql_search import sql
import functools
auth_app = Blueprint('auth',__name__)
@auth_app.route('/login/',methods=['GET','POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
user = request.form.get('name')
pwd = request.form.get('pwd')
sql_info = 'SELECT username,password FROM userinfo'
db_info = sql(sql_info)
for i in db_info:
if user == i[0] and pwd == i[1]:
session['xxx'] = user #设置session
return redirect('/article/list')
else:
return redirect('/login/')
article_app
from flask import Blueprint,render_template,request,redirect,session
from bms_flask.static.sql_search import sql
from bms_flask.views.login import auth
article_app = Blueprint('article',__name__)
@article_app.route('/article/list')
def article():
sql_info = 'SELECT * FROM article'
db_info = sql(sql_info)
return render_template('article.html',data_dict=db_info)
init.py
from flask import Flask
from bms_flask.views.login import auth_app
from bms_flask.views.article import article_app
def create_app():
app = Flask(__name__)
app.secret_key = 'nvfjdvnfdjvk' #session 的秘钥
app.register_blueprint(auth_app) #注册的蓝图,url_prefix是一个参数,是url的前缀
#app.register_blueprint(auth_app,url_prefix='/web')
#在访问的时候url就为 xxx.xx/web/xx
app.register_blueprint(article_app) #注册的蓝图
return app
flask与装饰器
例如程序中设置了session,给需要认证的视图加认证判断,通过装饰器来写.
# 装饰器
from flask import Blueprint,render_template,request,redirect,session
from bms_flask.static.sql_search import sql
import functools
auth_app = Blueprint('auth',__name__)
def auth(func):
@functools.wraps(func) # 必须写,这时候执行__name__,结果就是被装饰的函数名,而不是inner,如果不加funtools,__name__就是inner,以后多个装饰器会造成url别名重复
def inner(*args,**kwargs):
user = session.get('xxx')
if not user:
return redirect('/login/')
return func(*args,**kwargs)
return inner
# 加认证的视图
from flask import Blueprint,render_template,request,redirect,session
from bms_flask.static.sql_search import sql
from bms_flask.views.login import auth
article_app = Blueprint('article',__name__)
@article_app.route('/article/del/',methods=['GET','POST'])
@auth # 一定要写在路由下边
def remove():
pk = request.args.get('pk')
sql('delete from article where id=%s'%pk)
return redirect('/article/list')
flask的session(保持会话)
flask的session是基于cookie的会话保持
session保持在浏览器
from flask import session #导入session
from flask import Flask
def create_app():
app = Flask(__name__)
app.secret_key = 'nvfjdvnfdjvk' # session 的秘钥,不写会报错,是一个随机字符串
return app
session['xxx'] = user # 设置session
user = session.get('xxx') #获取session
数据连接池
当有多个函数需要用到sql语句时,需要多次操作数据库,就会多次的连接关闭数据库,会影响性能
连接池实现原理:创建一个链接池,维护与数据库的连接,使用时来进行获取,使用完毕后在放回到连接池。
安装dbutils和pymysql
pip install dbutils
pip install pymysql
使用
函数版的连接池
import pymysql
from DBUtils.PooledDB import PooledDB
POOL = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的链接,0表示不创建
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
ping=0,
# ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='flask_db1',
charset='utf8',
autocommit=True #提交sql语句
)
def sql(sql_info):
conn = POOL.connection()
sql1 = sql_info
cursor1 = conn.cursor()
cursor1.execute(sql1)
r_all = cursor1.fetchall()
cursor1.close()
return r_all
类版的连接池
import pymysql
from DBUtils.PooledDB import PooledDB
class SqlHelp(object):
def __int__(self,*args,**kwargs):
self.pool = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的链接,0表示不创建
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
ping=0,
# ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='flask_db1',
charset='utf8'
)
def open(self):
conn = self.pool.connection()
cursor1 = conn.cursor()
return conn,cursor1
def close(self,cursor1,conn):
cursor1.close()
conn.close()
def sql(self,sql_info):
conn,cursor1 = self.open()
cursor1.execute(sql_info)
r_all = cursor1.fetchall()
self.close(cursor1,conn)
return r_all
db = SqlHelp()
flask静态文件处理
如果存放静态文件的文件名不是static,例如文件夹名为sssss,就需要写app = Flask(__name__,static_folder='sssss')
from flask import Flask,render_template
app = Flask(__name__,static_folder='static',static_url_path='/static')
#static是存放配置文件的文件夹名,static_url_path是在路径的别名,不设置static_url_path之前在前端中这样写<img src="/static/index.png" alt="">,加了别名之后<img src="/img/index.png" alt="">
建议前端img地址写法:<img src="{{ url_for('static',filename='index.png') }}" alt="">
static下的index.png
@app.route('/index')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()
WSGI源码分析
1.程序启动,等待用户请求到来
app.run() >>执行 run_simple(host, port, self, **options)# self就是我们创建的flask app
2.用户请求到来会执行app.__call__方法,主要看app.__call__返回了什么
2.1__call__返回了self.wsgi_app(environ, start_response)
看wsgi_app返回了什么
2.2 进到wsgi_app函数,其他的不看,就看该函数返回了什么,返回了response(environ, start_response),主要看response哪来的,自己就可以导入response,并返回
2.3看response哪来的,上面有response..
response = self.full_dispatch_request()
看full_dispatch_request返回什么response就是什么
它返回了return self.finalize_request(rv)又调用了finalize_request(rv),rv就是视图的返回值,是个字符串
2.4看finalize_request函数
response = self.make_response(rv)
2.5在看make_response返回了什么
#跟据自己的内容进行判断
rv = self.response_class(rv, status=status, headers=headers)
response_class = Response
在这里进行一系列的判断并返回rv
flask路由系统
路由系统的两种写法
# --第一种
from flask import Flask,render_template
app = Flask(__name__,static_folder='static')
@app.route('/index')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()
# --第二种
from flask import Flask,render_template
app = Flask(__name__,static_folder='static')
def index():
return render_template('index.html')
app.add_url_rule('/index','index',index) #分别代表路径,别名,视图函数名
if __name__ == '__main__':
app.run()
路由加载的源码流程
- 将url和函数打包成为 rule 对象
- 将rule对象添加到map对象中。
- app.url_map = map对象
把路由和函数的对应关系放在flask的对象里,这个对象里保存了路由和函数的对应关系,如果有多个视图和路由, 将url和函数打包成为 rule 对象
(放在Map对象里),可以通过app.url_map可以拿到map对象,对象里存放的
就是url和函数名
先执行route,返回了decorator,然后就执行decorator函数,内部执行了add_url_rule,帮助把url和函数的对应关系打包成为 rule 对象,放在Map对象中
endpoint不写为什么默认是函数本身?
源码:
1.执行route,返回decorator
2.
def decorator(f): #f就是被装饰的函数
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
3.执行add_url_rule
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
4.执行_endpoint_from_view_func(view_func)
def _endpoint_from_view_func(view_func):
assert view_func is not None, "expected view func if endpoint is not provided."
return view_func.__name__ #返回的函数名
动态路由
@app.route('/login')
def login():
return render_template('login.html') @app.route('/login/<name>')
def login(name):
print(type(name)) return render_template('login.html') @app.route('/login/<int:name>') #数据类型的转换
def login(name):
print(type(name))
return render_template('login.html')
flask的正则表达式的路由
from flask import Flask,render_template
app = Flask(__name__)
from werkzeug.routing import BaseConverter
class RegConverter(BaseConverter):
def __init__(self, map, regex):
super().__init__(map)
self.regex = regex
app.url_map.converters['regex'] = RegConverter @app.route('/index/<regex("d+"):x1>')
def index(x1):
return render_template('index.html')
if __name__ == '__main__':
app.run()
flask视图
FBV
def index():
return render_template('index.html') app.add_url_rule('/index', 'index', index)
@app.route('/login')
def login():
return render_template('login.html')
CBV
from flask import Flask,render_template,views
app = Flask(__name__,)
def test1(func):
def inner(*args,**kwargs):
print('before1')
result = func(*args,**kwargs)
print('after1')
return result
return inner
def test2(func):
def inner(*args,**kwargs):
print('before2')
result = func(*args,**kwargs)
print('after2')
return result
return inner
class UserView(views.MethodView):
methods = ['GET',"POST"]
decorators = [test1,test2]
def get(self):
print('get')
return 'get'
def post(self):
print('post')
return 'post'
app.add_url_rule('/user',view_func=UserView.as_view('user')) #路由写法
if __name__ == '__main__':
app.run()
模板
基本用法
from flask import Flask,render_template
app = Flask(__name__)
def fun(arg):
return '你好'+arg
@app.route('/index')
def index():
nums = [11,22,33,44]
return render_template('index.html',nums=nums,f=fun)
#传递给前端函数的地址
if __name__ == '__main__':
app.run()
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>index页面</h1>
{{nums}}
{{f("xx")}} 调用函数,并传递参数
<img src="{{ url_for('static',filename='index.png') }}" alt="">
</body>
</html>
继承
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/home')
def home():
return render_template('home.html')
if __name__ == '__main__':
app.run()
layout.html模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>模板</h1>
{% block content %}
{% endblock %}
</body>
</html>
home.html
{% extends 'layout.html' %}
{% block content %}
<input type="text">
{% endblock %}
定义全局模板的方法
from flask import Flask,render_template
app = Flask(__name__)
@app.template_global() #定义全局
def func(arg):
return '等会' + arg
@app.route('/index')
def index():
return render_template('index.html') #这就不用传给前端函数名
if __name__ == '__main__':
app.run()
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>index页面</h1>
{{func('你')}}
<img src="{{ url_for('static',filename='index.png') }}" alt="">
</body>
</html>
flask中的特殊装饰器
第一种情况
from flask import Flask,render_template,request
app = Flask(__name__)
@app.before_request
def f1():
if request.path == '/login':
return
print('f1')
@app.after_request
def f10(response):
print('f10')
return response
@app.route('/index')
def index():
print('index')
return render_template('index.html')
if __name__ == '__main__':
app.run()
结果:
# f1
# index
# f10
可以参考django的中间件的process_request和process_response的执行顺序
第二种情况
from flask import Flask,render_template,request
app = Flask(__name__)
@app.before_request
def f1():
if request.path == '/login':
return
print('f1')
return '123'
@app.after_request
def f10(response):
print('f10')
return response
@app.route('/index')
def index():
print('index')
return render_template('index.html')
if __name__ == '__main__':
app.run()
结果:
# f1
# f10
多个装饰器
from flask import Flask, render_template, request
app = Flask(__name__)
@app.before_request
def f1():
print('f1')
@ app.before_request
def f2():
print('f2')
@ app.after_request
def f10(response):
print('f10')
return response
@ app.after_request
def f20(response):
print('f20')
return response
@ app.route('/index')
def index():
print('index')
return render_template('index.html')
if __name__ == '__main__':
app.run()
app.__call__
结果:
f1
f2
index
f20
f10