1、配置文件

default_config = ImmutableDict( { "ENV": None, "DEBUG": None, "TESTING": False, "PROPAGATE_EXCEPTIONS": None, "PRESERVE_CONTEXT_ON_EXCEPTION": None, "SECRET_KEY": None, "PERMANENT_SESSION_LIFETIME": timedelta(days=31), "USE_X_SENDFILE": False, "SERVER_NAME": None, "APPLICATION_ROOT": "/", "SESSION_COOKIE_NAME": "session", "SESSION_COOKIE_DOMAIN": None, "SESSION_COOKIE_PATH": None, "SESSION_COOKIE_HTTPONLY": True, "SESSION_COOKIE_SECURE": False, "SESSION_COOKIE_SAMESITE": None, "SESSION_REFRESH_EACH_REQUEST": True, "MAX_CONTENT_LENGTH": None, "SEND_FILE_MAX_AGE_DEFAULT": timedelta(hours=12), "TRAP_BAD_REQUEST_ERRORS": None, "TRAP_HTTP_EXCEPTIONS": False, "EXPLAIN_TEMPLATE_LOADING": False, "PREFERRED_URL_SCHEME": "http", "JSON_AS_ASCII": True, "JSON_SORT_KEYS": True, "JSONIFY_PRETTYPRINT_REGULAR": False, "JSONIFY_MIMETYPE": "application/json", "TEMPLATES_AUTO_RELOAD": None, "MAX_COOKIE_SIZE": 4093, } )
配置方式1:
app = Flask(__name__) app.config["DEBUG"] = True
config继承字典,所以字典有方法它都能用
配置方式2:

class Foo(object): DEBUG = False TEMPLATES = "templates" class Dev(object): DEBUG = True TEMPLATES = "templates"
app = Flask(__name__) # app.config.from_object("settings.Foo") app.config.from_object("settings.Dev")
更多配置方式:

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目录 配置文件
2、路由系统
@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'])
常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:
DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }

rule, URL规则 view_func, 视图函数名称 endpoint = None, 名称,用于反向生成URL,即: url_for('名称') methods = None, 允许的请求方式,如:["GET", "POST"] strict_slashes = None, 对URL最后的 / 符号是否严格要求, redirect_to = None, 重定向到指定地址 defaults = None, 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'} 为函数提供参数 subdomain = None, 子域名访问
简单使用:
@app.route('/index/<username>') def index(username): print(username) return "index" # @app.route('/index/<int:id>') # def index(id): # print(id) # return "index"

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' 或 def index(): return "Index" self.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"]) or app.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"]) app.view_functions['index'] = index 或 def auth(func): def inner(*args, **kwargs): print('before') result = func(*args, **kwargs) print('after') return result return inner class IndexView(views.View): methods = ['GET'] decorators = [auth, ] def dispatch_request(self): print('Index') return 'Index!' app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name=endpoint 或 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() a.注册路由原理

from flask import Flask, views, url_for from werkzeug.routing import BaseConverter app = Flask(import_name=__name__) class RegexConverter(BaseConverter): """ 自定义URL匹配正则表达式 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 路由匹配时,匹配成功后传递给视图函数中参数的值 :param value: :return: """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 :param value: :return: """ val = super(RegexConverter, self).to_url(value) return val # 添加到flask中 app.url_map.converters['regex'] = RegexConverter @app.route('/index/<regex("d+"):nid>') def index(nid): print(url_for('index', nid='888')) return 'Index' if __name__ == '__main__': app.run() b. 自定制正则路由匹配
反向生成:url_for
from flask import Flask, url_for
endpoint("name") #别名,相当于django中的name
@app.route('/') def index1(): url = url_for("aaa") print(url) # /set return "Index1" @app.route('/set', endpoint="aaa") def index2(): return 'ok'
通过add_url_rule方法添加路由:因为route内部就是调用的flask这个方法,所以我们可以在外部直接覆盖它
def list_student(nid): print(nid) return render_template("student.html") app.add_url_rule('/student', None, list_student)
3、视图
FBV:
方式一: @app.route('/index',endpoint='xx') def index(nid): url_for('xx',nid=123) return "Index" 方式二: def index(nid): url_for('xx',nid=123) return "Index" app.add_url_rule('/index',index)
CBV:
def auth(func): def inner(*args, **kwargs): result = func(*args, **kwargs) return result return inner class IndexView(views.MethodView): # methods = ['POST'] #只允许POST请求访问 decorators = [auth,] #如果想给所有的get,post请求加装饰器,就可以这样来写,也可以单个指定 def get(self): #如果是get请求需要执行的代码 v = url_for('index') print(v) return "GET" def post(self): #如果是post请求执行的代码 return "POST" app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) #name指定的是别名,会当做endpoint使用 if __name__ == '__main__': app.run()
4、请求和响应相关
请求相关:

# 请求相关信息 # request.method # 请求方式 # request.args # 获取路径中的条件 ?username=aike # request.form # 获取post请求的数据 # request.values # request.cookies #获取cookie和session # 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') # 删除响应头cookies # response.set_cookie('key', 'value') # response.headers['X-Something'] = 'A value' #设置响应头 # return response
5、模板渲染
使用:Flask使用的是Jinja2模板,所以其语法和Django无差别,并且更接近python原生语法
母版、块、过滤器等,使用无区别:参考django
他可以传入函数在模板中使用:
Flask中自定义模板方法的方式和Bottle相似,创建一个函数并通过参数的形式传入render_template,如:
且在模板当中,函数支持传入参数

<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <h1>自定义函数</h1> {{ww()|safe}} </body> </html>

#!/usr/bin/env python # -*- coding:utf-8 -*- from flask import Flask,render_template app = Flask(__name__) def func(): return '<h1>Wupeiqi</h1>' @app.route('/login', methods=['GET', 'POST']) def login(): return render_template('login.html', ww=func) if __name__ == '__main__': app.run()

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% macro input(name, type='text', value='') %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> {% endmacro %} {{ input('n1') }} {% include 'tp.html' %} <h1>asdf{{ v.k1}}</h1> </body> </html>
六、session
除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。
app.secret_key = "lllasdka" # 设置秘钥
设置:session['username'] = 'xxx' 删除:session.pop('username', None) del session['username']

from flask import Flask, render_template, redirect, request, session import functools app = Flask(__name__) app.secret_key = "lllasdka" def func(f): @functools.wraps(f) def inner(*args, **kwargs): if not session.get("token"): return redirect("/login") ret = f(*args, **kwargs) del session["token"] return ret return inner @app.route('/index') @func def index(): return render_template("index.html") @app.route('/login', methods=["GET", "POST"]) def login(): if request.method == "GET": return render_template("index.html") user = request.form.get("user") pwd = request.form.get("pwd") if user == "aike" and pwd == "666": session["token"] = 1 return "登录成功" return render_template("index.html", error="用户名或密码错误") @app.route("/index2") @func def index2(): return "ok" if __name__ == '__main__': app.run()

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)

#!/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()
7、message
message是一个基于Session实现的用于保存数据的集合,其特点是:使用一次就删除。实现原理就是每请求一次就像session中添加一条值在一个列表中,取一次就pop这条session
from flask import Flask, render_template, redirect, request, session ,get_flashed_messages, flash app = Flask(__name__) app.secret_key = "lllasdka" app.config.from_object("settings.Foo") @app.route('/') def index1(): messages = get_flashed_messages() # 取出存入的值 print(messages) # 1 return "Index1" @app.route('/set') def index2(): v = request.args.get('p') # ?p=1 flash(v) return 'ok'
8、中间件
与django的中间件不同,中间件的执行在路由之前,在flask当中,__call__是整个请求的流程的开始,它返回wsgi_app的执行,所以我们可以重写它进行请求前的操作
from flask import Flask, flash, redirect, render_template, request app = Flask(__name__) app.secret_key = 'some_secret' @app.route('/') def index1(): return render_template('index.html') @app.route('/set') def index2(): v = request.args.get('p') flash(v) return 'ok' class MiddleWare: def __init__(self,wsgi_app): self.wsgi_app = wsgi_app def __call__(self, *args, **kwargs): return self.wsgi_app(*args, **kwargs) if __name__ == "__main__": app.wsgi_app = MiddleWare(app.wsgi_app) app.run(port=9999)
9、蓝图
建议阅读官方文档:跳转

Flask 中蓝图有以下用途:
把一个应用分解为一套蓝图。这是针对大型应用的理想方案:一个项目可以实例化 一个应用,初始化多个扩展,并注册许多蓝图。
在一个应用的 URL 前缀和(或)子域上注册一个蓝图。 URL 前缀和(或)子域的 参数成为蓝图中所有视图的通用视图参数(缺省情况下)。
使用不同的 URL 规则在应用中多次注册蓝图。
通过蓝图提供模板过滤器、静态文件、模板和其他工具。蓝图不必执行应用或视图 函数。
当初始化一个 Flask 扩展时,为以上任意一种用途注册一个蓝图。
Flask 中的蓝图不是一个可插拨的应用,因为它不是一个真正的应用,而是一套可以 注册在应用中的操作,并且可以注册多次。那么为什么不使用多个应用对象呢?可以 使用多个应用对象(参见 应用调度 ),但是这样会导致每个应用都使 用自己独立的配置,且只能在 WSGI 层中管理应用。
而如果使用蓝图,那么应用会在 Flask 层中进行管理,共享配置,通过注册按需改 变应用对象。蓝图的缺点是一旦应用被创建后,只有销毁整个应用对象才能注销蓝图。

蓝图的基本概念是:在蓝图被注册到应用之后,生成所要执行的操作的集合。当分配请求时, Flask 会把蓝图和视图函数关联起来,并生成相对应的URL 。
使用:
1、创建蓝图对象:导入模块Blueprint

name -在名称蓝图。将在每个端点名称之前。
import_name – 蓝图包的名称,通常为 __name__。这有助于找到root_path该蓝图。
static_folder –包含静态文件的文件夹,该文件应由蓝图的静态路由提供。该路径相对于蓝图的根路径。蓝图静态文件默认为禁用。
static_url_path –用于提供静态文件的URL。默认为static_folder。如果该蓝图没有url_prefix,则该应用程序的静态路由将优先,并且该蓝图的静态文件将不可访问。
template_folder –带有模板的文件夹,应将其添加到应用程序的模板搜索路径中。该路径是相对于 蓝图的根路径。蓝图模板默认为禁用。蓝图模板的优先级低于应用程序模板文件夹中的优先级。
url_prefix –前缀在所有蓝图 URL上的路径,以使它们与应用程序的其余路由区分开。
子域 – 默认情况下,蓝图路由将匹配的子域。
url_defaults –默认情况下,蓝图路由将接收的默认值。
root_path –默认情况下,蓝图将基于import_name。在某些情况下,此自动检测可能会失败,因此可以手动指定路径。
from flask import Blueprint, render_template student = Blueprint("student", __name__, template_folder="templates", static_folder="statics", static_url_path="static") # student = Blueprint("student", __name__, static_url_path="static") @student.before_request # 只对当前蓝图对象有效 def xxx(): print("sss") @student.route("/student") def list_student(): return render_template("student.html")
2、注册蓝图

blueprint:必须给定,需要注册的一个蓝图对象 url_prefix:给请求的路由添加一个前缀:127.0.0.1:8000/api/course subdomain:路由将匹配这个子域。 url_defaults: 给路由匹配的视图函数添加默认值 options:传递额外的关键字参数
from flask import Flask from .views.course import course from .views.student import student from .views.userinfo import user app = Flask(__name__) @app.before_request # 对所有注册的蓝图都有效 def xx(): print("xxxxx") app.register_blueprint(course, url_prefix="/api") app.register_blueprint(student, url_prefix="") app.register_blueprint(user, url_prefix="")
使用蓝图应该注意的地方:
创建蓝图对象时传入的static_folder与template_folder参数,默认还是先找Flask设置的默认参数,flask没有再找蓝图设置的。
注册蓝图时的url_prefix参数不传时,可能会报错,错误信息为“”urls must start with a leading slash“”,最好是传入一个空字符串
若想为单独的一类的视图使用before_request、after_request等特殊的装饰器时,需要在这类视图中通过蓝图对象添加装饰器即可,而不是flask对象。flask对象使用的特殊装饰器对全局有效
10、特殊装饰器
before_request:视图执行前需要执行的函数
after_request:视图执行完毕后需要执行的函数
before_first_request:第一次请求执行的函数,后面的请求不再执行
template_global:全局定义函数,所有的视图不传入这个函数也可以在模板中使用:{{函数名(参数1,参数2)}}
template_filter:与global一样,只是使用方式不一样,但是这个能在模板中当做是if条件:{{ 参数1 | 函数名(参数2,参数3)}}
errorhandler:自定义错误信息如何在前端展示

#!/usr/bin/env python # -*- coding:utf-8 -*- from flask import Flask, Request, render_template app = Flask(__name__, template_folder='templates') app.debug = True @app.before_first_request def before_first_request1(): print('before_first_request1') @app.before_first_request def before_first_request2(): print('before_first_request2') @app.before_request def before_request1(): Request.nnn = 123 print('before_request1') @app.before_request def before_request2(): print('before_request2') @app.after_request def after_request1(response): print('before_request1', response) return response @app.after_request def after_request2(response): print('before_request2', response) return response @app.errorhandler(404) def page_not_found(error): return 'This page does not exist', 404 @app.template_global() def sb(a1, a2): return a1 + a2 @app.template_filter() def db(a1, a2, a3): return a1 + a2 + a3 @app.route('/') def hello_world(): return render_template('hello.html') if __name__ == '__main__': app.run()