Flask 框架
Django: 重武器,内部包含了非常多的组件,ORM,FORM,MODELform,缓存,中间件,信号 Flask: 短小精悍 内部没有包含太多组件,第三方组件丰富,能伸能缩,能大能小 Tornado: 异步非阻塞 (node.js) Bottle: 框架更小 企业级的应用很少使用 Web.py: 框架小
安装:
pip3 install flask
基本使用:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
配置文件:
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
{
'DEBUG': get_debug_flag(default=False), 是否开启Debug模式
'TESTING': False, 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None,
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'USE_X_SENDFILE': False,
'LOGGER_NAME': None,
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None,
'APPLICATION_ROOT': None,
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
}
方式一:
app.config['DEBUG'] = True
PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)
方式二:
app.config.from_pyfile("python文件名称")
如:
settings.py
DEBUG = True
app.config.from_pyfile("settings.py")
app.config.from_envvar("环境变量名称")
环境变量的值为python文件名称名称,内部调用from_pyfile方法
app.config.from_json("json文件名称")
JSON文件名称,必须是json格式,因为内部会执行json.loads
app.config.from_mapping({'DEBUG':True})
字典格式
app.config.from_object("python类或类的路径")
app.config.from_object('pro_flask.settings.TestingConfig')
settings.py
class Config(object):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite://:memory:'
class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config):
DEBUG = True
class TestingConfig(Config):
TESTING = True
PS: 从sys.path中已经存在路径开始写
PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录
常使用的配置文件存放方式:
from flask import Flask
app = Flask(__name__)
app.config.from_object("settings.DevelopmentConfig") # 配置类路径
@app.route('/')
def index():
return 'Hello World!'
if __name__ == '__main__':
app.run()
settings.py中:
class Config(object):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite://:memory:'
class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config):
DEBUG = True
class TestingConfig(Config):
TESTING = True
路由匹配:
方式一:
@app.route('/login',methods=['GET','POST'],endpoint='l1') # 别名=l1
def login():
pass
方式二:
app.add_url_rule('/index','in2',index, methods=['GET',"POST"])
# url 别名 视图函数 method方法
注: endpoint 不写的话使用视图函数名,endpiont 不可重复
路由系统
•@app.route('/user/<username>')
•@app.route('/post/<int:post_id>')
•@app.route('/post/<float:post_id>')
•@app.route('/post/<path:path>')
•@app.route('/login', methods=['GET', 'POST'])
常用路由系统有以上五种
为视图加装装饰器:
FBV模式
def auth(func):
def inner(*args, **kwargs):
print('before')
result = func(*args, **kwargs)
print('after')
return result
return inner
@app.route('/index.html', methods=['GET', 'POST'], endpoint='index')
@auth
def index():
return 'Index'
CBV模式
def auth(func):
def inner(*args, **kwargs):
print('before')
result = func(*args, **kwargs)
print('after')
return result
return inner
class IndexView(views.MethodView):
methods = ['GET']
decorators = [auth, ]
def get(self):
return 'Index.GET'
def post(self):
return 'Index.POST'
app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name=endpoint
@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最后的 / 符号是否严格要求,
如:
@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>')
或
def func(adapter, nid):
return "/home/888"
@app.route('/index/<int:nid>', redirect_to=func)
subdomain=None, 子域名访问
from flask import Flask, views, url_for
app = Flask(import_name=__name__)
app.config['SERVER_NAME'] = 'wupeiqi.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"
@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()
注:子域名访问:
项目中设置主域名和端口号:
app.config['SERVER_NAME'] = 'haha.com:5000' # 主域名 加 端口号
在host文件中设置 项目IP对应的域名:
127.0.0.1 www.haha.com # IP 对应 想要的哪些域名访问
127.0.0.1 lala.haha.com 127.0.0.1 hehe.haha.com
项目中设置 想要的域名访问的哪些视图函数
@app.route("/lala", subdomain="lala") # 域名为 lala.haha.com/lala 的url访问此视图
def lala_index():
return "lala.your-domain.tld"
@app.route("/niu", subdomain="<name>") # 在host中,项目IP对应的子域名都可以访问 .haha.com/niu 的url访问此视图
def niu_index(name): # 子域名
return name + ".your-domain.tld"
@app.route("/", subdomain="www")
def www_index():
return "www.your-domain.tld"
自定制正则表达式路由匹配
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter):
''' 自定义URL匹配正则表达式类 '''
def __init__(self,map,regex):
super().__init__(map)
self.regex = regex # 要写上
def to_python(self, value): # 方法名固定写法
print('url匹配成功后传进来的值,做处理后再传进视图函数中>>>',value) # 123
return int(value)
def to_url(self, value): # 方法名固定写法
print('使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数>>>',value) # abc
val = super().to_url(value)
return val
app.url_map.converters['regex'] = RegexConverter # 添加进flask对象中
@app.route('/test/<regex("\d+"):nid>')
def test(nid):
print(url_for('test', nid='abc')) # url别名为test,若没有则使用视图函数名 ; nid 用于反向解析的参数 /test/abc
return 'Index'
模板:Flask使用的是Jinja2模板,所以其语法和Django无差异
自定义模板方法: 创建一个函数并通过参数的形式传入render_template
def func1(arg):
return Markup("<input type='text' value='%s' />" % (arg,)) # flask自带防止xss攻击 所以在写入标签时 要使用Markup() 也可以在前端加 |safe
@app.route('/login',methods=['GET','POST'],endpoint='l1',subdomain="www")
def login():
if request.method == "GET":
return render_template('login.html',**{'func1':func1})
前端
{{ func1('哈哈哈') }}
请求与响应
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
# request.form
# request.values
# request.cookies
# request.headers
# request.path
# request.full_path
# request.script_root
# request.url
# request.base_url
# request.url_root
# request.host_url
# request.host
# 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')
# 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()
session
除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。
-
设置:session['username'] = 'xxx'
- 删除:session.pop('username', None)
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
@app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
@app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
基本使用
pip3 install Flask-Session
run.py
from flask import Flask
from flask import session
from pro_flask.utils.session import MySessionInterface
app = Flask(__name__)
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
app.session_interface = MySessionInterface()
@app.route('/login.html', methods=['GET', "POST"])
def login():
print(session)
session['user1'] = 'alex'
session['user2'] = 'alex'
del session['user2']
return "内容"
if __name__ == '__main__':
app.run()
session.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import uuid
import json
from flask.sessions import SessionInterface
from flask.sessions import SessionMixin
from itsdangerous import Signer, BadSignature, want_bytes
class MySession(dict, SessionMixin):
def __init__(self, initial=None, sid=None):
self.sid = sid
self.initial = initial
super(MySession, self).__init__(initial or ())
def __setitem__(self, key, value):
super(MySession, self).__setitem__(key, value)
def __getitem__(self, item):
return super(MySession, self).__getitem__(item)
def __delitem__(self, key):
super(MySession, self).__delitem__(key)
class MySessionInterface(SessionInterface):
session_class = MySession
container = {}
def __init__(self):
import redis
self.redis = redis.Redis()
def _generate_sid(self):
return str(uuid.uuid4())
def _get_signer(self, app):
if not app.secret_key:
return None
return Signer(app.secret_key, salt='flask-session',
key_derivation='hmac')
def open_session(self, app, request):
"""
程序刚启动时执行,需要返回一个session对象
"""
sid = request.cookies.get(app.session_cookie_name)
if not sid:
sid = self._generate_sid()
return self.session_class(sid=sid)
signer = self._get_signer(app)
try:
sid_as_bytes = signer.unsign(sid)
sid = sid_as_bytes.decode()
except BadSignature:
sid = self._generate_sid()
return self.session_class(sid=sid)
# session保存在redis中
# val = self.redis.get(sid)
# session保存在内存中
val = self.container.get(sid)
if val is not None:
try:
data = json.loads(val)
return self.session_class(data, sid=sid)
except:
return self.session_class(sid=sid)
return self.session_class(sid=sid)
def save_session(self, app, session, response):
"""
程序结束前执行,可以保存session中所有的值
如:
保存到resit
写入到用户cookie
"""
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
expires = self.get_expiration_time(app, session)
val = json.dumps(dict(session))
# session保存在redis中
# self.redis.setex(name=session.sid, value=val, time=app.permanent_session_lifetime)
# session保存在内存中
self.container.setdefault(session.sid, val)
session_id = self._get_signer(app).sign(want_bytes(session.sid))
response.set_cookie(app.session_cookie_name, session_id,
expires=expires, httponly=httponly,
domain=domain, path=path, secure=secure)
自定义Session
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
pip3 install redis
pip3 install flask-session
"""
from flask import Flask, session, redirect
from flask.ext.session import Session
app = Flask(__name__)
app.debug = True
app.secret_key = 'asdfasdfasd'
app.config['SESSION_TYPE'] = 'redis'
from redis import Redis
app.config['SESSION_REDIS'] = Redis(host='192.168.0.94',port='6379')
Session(app)
@app.route('/login')
def login():
session['username'] = 'alex'
return redirect('/index')
@app.route('/index')
def index():
name = session['username']
return name
if __name__ == '__main__':
app.run()
第三方session
message 闪现
message 是基于session实现的用于保存数据的集合,
基于session所以不同用户是拿不到彼此message中的数据,
在message中获取的数据是一次性获取完的,再获取为空列表
from flask import Flask,flash,get_flashed_messages,session,redirect,request app = Flask(__name__) app.secret_key = 'lalala' # 闪现是基于session做的,根据用户的sessiong进行隔离 @app.route('/get') def get(): # 从摸个地方获取设置过的所有值,并清除 data = get_flashed_messages() # 一次性取出set进去的所有值 取出的是列表形式 若取不到值则为空列表 # data = get_flashed_messages(category_filter=["x1"]) # 一次性取出set进 x1 分类的所有值 print(data) # 再去取则为空[] return 'Hello' @app.route('/set') def set(): # 向某个地方设置值 # flash('abc') # 可以多次向其中赋值 flash('abc',category='x1') # 往x1分类中添加值 return 'Hello' if __name__ == '__main__': app.run()
请求的扩展: 类似于django中的中间件操作
@app.before_request() ===> 类似于 def process_request(): 请求到视图函数前,先做什么
可以基于此来做用户验证:
from flask import Flask,request,session,redirect app = Flask(__name__) app.secret_key = 'asdf' @app.before_request # 在执行相应的视图函数前,先执行before_request中的函数 def before1(): #print(request.url) # http://127.0.0.1:5000/login #print(request.path) # /login print('123') if request.path == '/login': # 增加白名单 return # 返回 None 表示通过验证 与 django中的中间件一样 if session.get('user_info'): return return redirect('/login') # session中无数据直接跳转到 login @app.route('/index') # 第一次访问index 无法访问 def index(): print('index') return 'index' @app.route('/login') def login(): print('login') session['user_info'] = 'haha' # 通过验证代码省略,通过验证后 session中设置值 用于验证 return 'login' if __name__ == '__main__': app.run()
before_request 与 after_request 加载运行过程: before 先加载先执行 after 先加载后执行
from flask import Flask app = Flask(__name__) @app.before_request def before1(): print('before_1') # return 'aaa' @app.before_request def before2(): print('before_2') return @app.after_request def after1(response): print('after_1') return response @app.after_request def after2(response): print('after_2') return response @app.route('/index') def index(): print('index') return 'index' if __name__ == '__main__': app.run() ''' before 先加载先执行 after 先加载后执行 before_1 before_2 index after_2 after_1 若 before中有返回值 视图不执行 但after全部执行 与django旧版本类似 '''
before_first_request 只有第一次执行时起作用,用于初始化设置
@app.before_first_request def before_first(): print('before_first===>')
错误信息定制 (请求的错误捕捉并自定义)
from flask import Flask app = Flask(__name__) @app.errorhandler(404) # 对错误信息捕捉 def error_404(arg): return '404 错误信息处理' if __name__ == '__main__': app.run()
模板自定义函数
from flask import Flask,render_template,Markup app = Flask(__name__) @app.template_global() def global_nb(a): return Markup('<input type="text" value=%s>'%(a,)) @app.template_filter() def filter_nb(a,b,c): return a+b+c @app.route('/index') def index(): return render_template('login.html') if __name__ == '__main__': app.run()
前端 <div> {{global_nb('哈哈哈')}} </div> <hr> <div> {{'我'|filter_nb('和','你')}} </div>

Flask 中间件: 其实请求扩展中已经做到了中间件的功能 Flask中间件只作为了解
from flask import Flask app = Flask(__name__) @app.route('/index') def index(): print('index') return 'index' class Md(object): def __init__(self,old_wsgi_app): self.old_wsgi_app = old_wsgi_app def __call__(self, environ, start_response): print('===>开始前') ret = self.old_wsgi_app(environ, start_response) print('===>结束后') return ret if __name__ == '__main__': app.wsgi_app = Md(app.wsgi_app) app.run()
蓝图: blueprint
当项目需求越来越多,app.py中的函数越来越多,这时就需要把app.py中各方法进行拆分到不同py文件中,构造项目目录结构
我们可以自己进行构造项目目录结构
目录结构:
app.py
from views import app if __name__ == '__main__': app.run()
view中的__init__.py
from flask import Flask,render_template app = Flask(__name__) from . import index from . import user
view中的index.py 与 user.py
from . import app from flask import render_template @app.route('/acc/index') def index(): print('index') return render_template('index.html')
from . import app from flask import render_template @app.route('/user/user') def user(): print('user') return render_template('user.html')
但我们自己写的目录结构只是做了拆分,而且文件间又重复调用,并且功能太少
推荐使用 flask自带的 蓝图 来应用到项目中
简单应用目录(推荐使用)
目录结构:

app.py
from flask_pro import app if __name__ == '__main__': app.run()
view中的__init__.py
from flask import Flask app = Flask(__name__,template_folder='templates',static_folder='statics',static_url_path='/static') # 模板位置,静态文件位置,静态文件前缀 from .views.account import account from .views.blog import blog app.register_blueprint(account,url_prefix='/account') # 把各应用中的蓝图对象 注册到flask的app对象中 # url批量处理 类似于 django的路由分发 app.register_blueprint(blog)
view中的account.py 与 blog.py
from flask import render_template from flask import Blueprint account = Blueprint('account',__name__) @account.route('/login') def login(): print('login') return render_template('login.html')
from flask import Blueprint from flask import render_template blog = Blueprint('blog',__name__,url_prefix='/blog') # 构造蓝图对象 # url_prefix='/blog' 给文件中的url进行批量处理 后缀加/blog @blog.route('/index',) def index(): print('index') return render_template('index.html')
大型项目应用目录

run.py
from flask_pro import app if __name__ == '__main__': app.run()
flask_pro中的__init__.py文件
from flask import Flask from .web import web app = Flask(__name__) app.debug = True app.register_blueprint(web,url_prefix='/blog')
flask_pro中的web文件中的__init__.py文件
from flask import Blueprint web = Blueprint('web',__name__,template_folder='templates',static_folder='statics') from .views import index from .views import detail
flask_pro中的web文件中的index.py 与 detail.py
from .. import web from flask import render_template @web.route('/index') def index(): print('index') return render_template('index.html')
from .. import web from flask import render_template @web.route('/detail/<username>') def detail(username): print('detail'+username) return render_template('detail.html',**{'username':username})
Flask 上下文
单进程多线程 : threading.local() 对象 为每个线程开辟自己的内存空间,来保存自己的值,线程之间保持独立
import threading local_values = threading.local() # 用于为每个线程开辟一块内存空间保存其独有的值 def func(num): local_values.name = num import time time.sleep(1) print(local_values.name, threading.current_thread().name) for i in range(20): th = threading.Thread(target=func, args=(i,), name='线程%s' % i) th.start()
为了使每个线程彼此有独立的储存空间,也可以使用线程的唯一标识(get_ident())来创建一个独立的空间,储存其独有的值
import threading from _thread import get_ident class Local(): def __init__(self): self.storage = {} self.get_ident = get_ident def set(self,k,v): ident = self.get_ident() # 获取线程的唯一标识 origin = self.storage.get(ident) if not origin: origin = {k:v} else: origin[k] = v self.storage[ident] =origin def get(self,k): ident = self.get_ident() # 获取线程的唯一标识 origin = self.storage.get(ident) return origin[k] local_values = Local() def task(arg): local_values.set('name',arg) import time time.sleep(1) print(local_values.get('name'),threading.current_thread().name) for i in range(20): th = threading.Thread(target=task,args=(i,),name='%s 线程'%i) th.start()
单进程单线程(多协程): 单个线程下多个协程会共享资源 ,因此需要使用协程的唯一标(getcurrent())识来创建独立的空间
import threading from greenlet import getcurrent as get_ident class Local(object): def __init__(self): self.storage = {} self.get_ident = get_ident def set(self,k,v): ident = self.get_ident() origin = self.storage.get(ident) if not origin: origin = {k:v} else: origin[k] = v self.storage[ident] = origin def get(self,k): ident = self.get_ident() origin = self.storage.get(ident) if not origin: return None return origin.get(k,None) local_values = Local() def task(num): local_values.set('name',num) import time time.sleep(1) print(local_values.get('name'), threading.current_thread().name) for i in range(20): th = threading.Thread(target=task, args=(i,),name='线程%s' % i) th.start()

浙公网安备 33010602011771号