Flask
一、基本使用
安装:
pip3 install flask
基本使用:
from flask import Flask, request, render_template, redirect, session app = Flask(__name__, template_folder="templates", static_folder="static") app.secret_key = "abc" @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "GET": return render_template("login.html") username = request.form.get("username") # 如果是get请求 request.args password = request.form.get("password") if username == "pd" and password == "123": session["user"] = username return redirect("/index") # return render_template("login.html", error="用户名或密码错误") return render_template("login.html", **{"error": "用户名或密码错误"}) @app.route("/index") def index(): user = session.get("user") if not user: return redirect("/login") return render_template("index.html") if __name__ == "__main__": app.run()
二、配置文件
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为: { 'DEBUG': False, # 是否开启Debug模式 'TESTING': False, # 是否开启测试模式 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), # session超时时间 '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, }
配置方式1:
app.config["DEBUG"] = True # 由于Config对象本质上是字典,所以还可以使用 app.config.update({"DEBUG": "True"})
配置方式2:
新建一个 settings.py 文件,例如:
# 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.config.from_object("配置文件类的路径")
所以,生产环境就用生产环境的配置,开发时就用开发环境的配置。
三、路由系统
@app.route("/index", endpoint="xx") # endpoint相当于别名 def index(): print(url_for("xx")) # /index url_for("xx"),反向生成url,默认url_for("函数名") return "index"
@app.route("/index/<user>") # 字符串 @app.route("/index/<int:id>") @app.route("/index/<float:id>") @app.route("/index/<path:path>") @app.route("/login", methods=["GET", "POST"]) # 示例: @app.route("/index/<user>") def index(user): return user # 常用路由系统有以上五种,所有的路由系统都是基于以下对应关系来处理: DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
反向生成带参数的URL,怎么做呢?
@app.route("/index/<int:id>") def index(id): print(url_for("index", id=id)) # id=1 生成固定参数的url return "index"
CBV
from flask import Flask, views, url_for app = Flask(__name__) def auth(func): @wraps(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:id>', redirect_to='/home/<id>') 或 def func(adapter, id): 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()
自定制正则路由匹配
from flask import Flask, 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+'):id>") def index(id): return url_for("index", id=id) if __name__ == "__main__": app.run()
四、请求和响应
from flask import Flask from flask import request from flask import render_template from flask import redirect from flask import make_response from flask import jsonify import os app = Flask(__name__) BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @app.route("/login", 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.get("the_file_name") # 拿到上传的文件名 obj.save(os.path.join(BASE_DIR, "files", obj.filename)) # 保存到本地路径 # 响应相关信息(响应体) return "字符串" data = {"key": "val"} return jsonify(data) # 相当于 json.dumps(data) return render_template("login.html", **{}) return redirect("/index") # 设置响应头 response = make_response(render_template("index.html")) # 将响应内容封装到这个对象中,再设置 # response是flask.wrappers.Response类型 response.delete_cookie("key") response.set_cookie("key", "val") response.headers["key"] = "val" return response if __name__ == "__main__": app.run()
五、Session
session 对象,允许在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,需要设置一个密钥。
设置:session["user"] = "xx" 获取:session.get("user")
session["user"] 删除:session.pop("user", None) # 可以在settings中设置秘钥 class DevelopmentConfig(Config): DEBUG = True SECRET_KEY = "asdf"
当请求刚到来:flask读取cookie中session对应的值,将该值解密并反序列化成字典,放入内存以便视图函数使用。
当请求结束时:flask会读取内存中字典的值,进行序列化、加密,写入到用户cookie中。
第三方session
""" pip3 install redis pip3 install flask-session """ import redis from flask import Flask, session from flask_session import Session # from flask.ext.session import Session # 老版本,同 from flask_session import Session app = Flask(__name__) app.config["SESSION_TYPE"] = "redis" app.config["SESSION_REDIS"] = redis.Redis(host="127.0.0.1",port=6379) Session(app) @app.route("/login") def login(): session["user"] = "username" return "login" @app.route("/index") def index(): print(session.get("user")) return "index" if __name__ == "__main__": app.run()
flash
临时存储数据,取了就没了;在session中存储一个数据,读取时通过pop将数据移除。
from flask import Flask, flash, get_flashed_messages app = Flask(__name__) @app.route('/test1') def test1(): flash('aa', 'error') # 第二个参数:分类 flash('bb', 'error') flash('cc', 'info') return "Set Session" @app.route('/test2') def test2(): print(get_flashed_messages(category_filter=['error'])) # ['aaaaa', 'bbbbb'] return "Get Session" if __name__ == "__main__": app.run()
六、模板
1、Flask 使用的是 Jinja2 模板,所以其语法和 Django 无差别。
{% for k,v in data.items() %} <tr> <td>{{ k }}</td> <td>{{ v.name }}</td> <td>{{ v["age"] }}</td> <td>{{ v.get("hobby", "默认") }}</td> </tr> {% endfor %}
@app.route("/test") def test(): txt1 = Markup("<h1>哈哈</h1>") # Markup相当于Django的mark_safe txt2 = "<h1>呵呵</h1>" return render_template("test.html", **{"txt1": txt1, "txt2": txt2}) ####################################################################### {{ txt1 }} {{ txt2|safe }}
@app.template_global() def func1(a1, a2): return a1+a2 @app.template_filter() def func2(a1, a2, a3): return a1+a2+a3 # 调用方式,模板中:{{ func1(1,2) }} {{ 1|func2(2,3) }}
2、自定义模板方法
from flask import Flask, render_template app = Flask(__name__) def func(): return '<h1>哈哈哈</h1>' @app.route('/test') def test(): return render_template('test.html', xx=func) if __name__ == "__main__": app.run() ################################################ <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>自定义函数</h1> {{ xx()|safe }} </body> </html>
macro
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {# 定义 #} {% macro func(name, type='text', value='') %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> {% endmacro %} {# 执行 #} {{ func('password', type='password', value=123456) }} </body> </html>
七、特殊装饰器
from flask import Flask app = Flask(__name__) @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(): print('before_request1') @app.before_request def before_request2(): print('before_request2') @app.after_request def after_request1(response): print('after_request1', response) return response @app.after_request def after_request2(response): print('after_request2', response) return response @app.route('/') def index(): return 'index' if __name__ == '__main__': app.run() # before_first_request1 # before_first_request2 # before_request1 # before_request2 # after_request2 <Response 5 bytes [200 OK]> # after_request1 <Response 5 bytes [200 OK]> ############################################ # 比较少的函数中需要额外添加功能,使用装饰器 def auth(func): @functools.wraps(func) def inner(*args, **kwargs): if not session.get('user'): return redirect(url_for('login')) ret = func(*args, **kwargs) return ret return inner @app.route('/index') @auth def index(): return 'index' # 给全局加上登录验证 @app.before_request def before_request(): if request.path == "/login": return None if session.get("user"): return None return redirect("login") ############################################ @app.errorhandler(404) def page_not_found(error): return '页面不见了'
八、蓝图
蓝图用于为应用提供目录划分。
小中型:
account.py
from flask import Blueprint from flask import render_template account = Blueprint("account", __name__) @account.route("/login", methods=["GET", "POST"]) def login(): return render_template("login.html")
course.py
from flask import Blueprint course = Blueprint("course", __name__)
__init__.py
from flask import Flask from .views.account import account from .views.course import course app = Flask(__name__, template_folder="templates", static_folder="statics", static_url_path="/static") app.register_blueprint(account) app.register_blueprint(course)
run.py
from pro_flask import app if __name__ == "__main__": app.run()
大型:
admin / __init__.py
from flask import Blueprint admin = Blueprint( "admin", __name__, template_folder="templates", static_folder="static" ) from . import views
admin / views.py
from . import admin @admin.route("/index") def index(): return "Admin.Index"
blog / __init__.py
from flask import Blueprint blog = Blueprint( "blog", __name__, template_folder="templates", static_folder="static" ) from . import view
blog / views.py
from . import blog @blog.route("/index") def index(): return "Blog.Index"
pro_flask / __init__.py
from flask import Flask from .admin import admin from .blog import blog app = Flask(__name__) app.debug = True app.register_blueprint(admin, url_prefix="/api") # url前缀 app.register_blueprint(blog)
run.py
from pro_flask import app if __name__ == "__main__": app.run()
url_prefix参数的作用:
http://127.0.0.1:5000/index # Blog.Index http://127.0.0.1:5000/api/index # Admin.Index
小中型应用程序目录:查看
大型应用程序目录:查看