Flask 基础
Falsk
Flask is a microframework for Python based on Werkzeug, Jinja 2 and good intentions.
“微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。
默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用。
Start Flask
pip insatll flask
werkzeug 简单应用
from werkzeug.wrappers import Request, Response
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, hello) # 开启本地 监听4000端口,绑定函数
Flask 简单应用
1.简单应用
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask
app = Flask(__name__)
app.debug = True
@app.route("/")
def sayhello():
return "hello word"
if __name__ == '__main__':
app.run()
2.配置文件系统
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目录
3.路由系统
# _*_ coding:utf-8 _*_
# __author__Zj__
# 路由系统 函数 视图
from flask import Flask
app = Flask(__name__)
app.debug = True
# 源码实例 router() 装饰器的原理
"""
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
"""
@app.route("/index")
def sayhello():
return "hello word"
def login():
return "login"
app.add_url_rule("/", "n2", login, methods=["GET", "POST"] )
if __name__ == '__main__':
app.run()
# 路由系统支持的 匹配模式
@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,
}
3.1 其次我们也可以自己注册自己的规则
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask
from werkzeug.routing import BaseConverter
app = Flask(__name__)
# 自定制正则路由匹配
class RegexConverter(BaseConverter):
def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex
def to_python(self, value):
"""
这里面对匹配到的字符串就行加工
:param value:
:return:
"""
return value
def to_url(self, value):
"""
反向生成url的时候需要用到
:param value:
:return:
"""
val = super(RegexConverter, self).to_python(value)
return val
app.debug = True
app.url_map.converters["regex"] = RegexConverter # 注册自己自己的url路有关系
@app.route("/")
def sayhello():
return "hello word"
@app.route("/index/<regex('xb\d+'):nid>", methods=["GET"])
def index(nid):
print(nid)
return "index"
if __name__ == '__main__':
app.run()
@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"
4.CBV 模式的分发规则
# _*_ coding:utf-8 _*_
# __author__Zj__
# 类视图的路由
from flask import Flask, views
app = Flask(__name__)
app.debug = True
def auth(func):
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
class IndexView(views.MethodView):
methods = ["GET", ] method 的list
decorators = [auth, ] # 装饰器的list
def get(self):
return "index.get"
def post(self):
return "index.get"
# 注册路由规则,加类 name 是 反向的url 名称
app.add_url_rule('/index', view_func=IndexView.as_view(name="index"))
if __name__ == '__main__':
app.run()
5.模板语言
1、模板的使用
Flask使用的是Jinja2模板,所以其语法和Django无差别。
2、自定义模板方法
Flask中自定义模板方法的方式和Bottle相似,创建一个函数并通过参数的形式传入render_template
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask, render_template, Markup, jsonify, make_response
app = Flask(__name__)
app.debug = True
def func(n):
return n + "sssss" # 自定义函数
def input(s):
text = "<input type='text' value=%s />" % s # 自定义input标签,用makeup来保证安全性
text = Markup(text)
return text
@app.route("/")
def sayhello():
return render_template("s7.html", **{"func": func, "input": input})
if __name__ == '__main__':
app.run(port=8001)
注意:Markup等价django的mark_safe
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--支持函数的使用-->
<h2>{{func("www")}}</h2>
{{input("11")}}
{{input("11")|safe}} # 也可以这样 如果后端不做makeup的时候 这样也说明是安全的
<!-- 宏定义 类似函数-->
{% macro input(name, type='text', value='') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
{{ input('n1') }}
</body>
</html>
5.1自定义模板语言
@app.template_global()
def sb(a1, a2):
return a1 + a2
@app.template_filter()
def db(a1, a2, a3):
return a1 + a2 + a3
使用:
{{sb(1,2)}} {{ 1|db(2,3)}}
5.2 cookies的设定
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask, render_template, Markup, jsonify, make_response
app = Flask(__name__)
app.debug = True
@app.route("/", methods=["GET"])
def index():
# text = "cookies"
# text = make_response(text)
# text.set_cookie("XXXX") #设置cookies
html = render_template("s7.html",text="1111")
response = make_response(html)
response.headers["xxx"] = "xxxx" # 设置headers
return response
if __name__ == '__main__':
app.run(port=8001)
6.请求相应相关参数
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存储
# 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 "string"
if __name__ == '__main__':
app.run()
6.1 Session相关
基本使用(lower)
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask, render_template, request, redirect, session, url_for
app = Flask(__name__)
app.debug = True
# app.config["SECRET_KEY"] = "1234"
app.secret_key = "123" # 开头需要加盐 这样才不会报错 两种形式
USERS = {
1: {'name': '老王', 'age': 18, 'gender': '男',
'text': "当眼泪掉下来的时候,是真的累了, 其实人生就是这样: 你有你的烦,我有我的难,人人都有无声的泪,人人都有难言的苦。 忘不了的昨天,忙不完的今天,想不到的明天,走不完的人生,过不完的坎坷,越不过的无奈,听不完的谎言,看不透的人心放不下的牵挂,经历不完的酸甜苦辣,这就是人生,这就是生活。"},
2: {'name': '老李', 'age': 28, 'gender': '男',
'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
3: {'name': '老鸨', 'age': 18, 'gender': '女',
'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
}
import functools
# 装饰器的使用,每次来验证session
# 因为有一点需要明确 就是 endpoint 不指定默认 用函数名
# 导致我们加装饰器的时候 每次返回的inners函数的名字不能一样 需要改成 传入的 func函数的名字
def verfriy(func):
@functools.wraps(func) # 函数属性转移
def inners(*args, **kwargs):
user = session.get("user_info")
if not user:
url = url_for("login")
return redirect(url)
return func(*args, **kwargs)
# inners.__name__ = func.__name__ # 也可以这样做
return inners
@app.route("/", methods=["GET", "POST"], endpoint="login")
def login():
if request.method == "GET":
return render_template("login.html", )
else:
user = request.form.get("user")
pwd = request.form.get("pwd")
if user == "alex" and pwd == "123":
session["user_info"] = user
return redirect("/index")
else:
return render_template("login.html", **{"msg": "erro"})
@app.route("/index", methods=["GET"])
@verfriy
def index():
return render_template("index.html", user_dict=USERS)
@app.route("/detail/<int:nid>", methods=["GET"])
@verfriy
def detail(nid):
user_info = USERS.get(nid)
return render_template("detail.html", user_info=user_info)
if __name__ == '__main__':
app.run(port=5001)
后续会添加自定义seesion
7. Flask 中的 蓝图
小型的目录结构:
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Blueprint, render_template
# 各个蓝图 view 里面的code
account = Blueprint("account", __name__, template_folder="templates",url_prefix="/account")
# 可重新 定义templates的文件夹,并且可以设置url的分组前缀(重要)
@account.route("/")
def index():
return render_template("actest.html")
大型的目录结构:
各个文件夹下的__init__方法的code
# -*- coding:utf-8 -*-
from flask import Blueprint
admin = Blueprint(
'admin',
__name__,
template_folder='templates',
static_folder='static'
)
from . import views # 视图函数
各个文件夹下的view 函数文件
# -*- coding:utf-8 -*-
from . import admin
@admin.route('/index')
def index():
return 'Admin.Index'
8. flash (message) 闪现
message是一个基于Session实现的用于保存数据的集合,其特点是:使用一次就删除。
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask, flash, get_flashed_messages, request, redirect
app = Flask(__name__)
app.debug = True
app.secret_key = "1111"
@app.route("/")
def index():
val = request.get_data("page")
if val == 'page':
return "page"
else:
flash("page not get !", category="erro") # 向flash中添加 messgae ,可以指定 category的类型,以便获取
return redirect("/erro")
@app.route("/erro")
def erro():
eval = get_flashed_messages(category_filter="erro")
# 是一个 列表的形式 取category 放进去的值
print(eval)
return "错误信息:" + eval[0]
if __name__ == '__main__':
app.__call__()
app.run()
9.中间件
9.1 基于flask 装饰器的中间件
before_request after_request
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask, render_template, request, redirect, session, url_for
app = Flask(__name__)
app.debug = True
app.secret_key = "123"
# 请求到来之前做session验证,不需要使用装饰器
# 请求扩展
USERS = {
1: {'name': '老张', 'age': 18, 'gender': '男',
'text': "当眼泪掉下来的时候,是真的累了, 其实人生就是这样: 你有你的烦,我有我的难,人人都有无声的泪,人人都有难言的苦。 忘不了的昨天,忙不完的今天,想不到的明天,走不完的人生,过不完的坎坷,越不过的无奈,听不完的谎言,看不透的人心放不下的牵挂,经历不完的酸甜苦辣,这就是人生,这就是生活。"},
2: {'name': '狼王', 'age': 28, 'gender': '男',
'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
3: {'name': '老鸨', 'age': 18, 'gender': '女',
'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
}
# 每次请求到来之前 执行before_request 装饰器函数,实现session的验证
@app.before_request
def process_request(*args,**kwargs):
print(request.path)
if request.path == '/':
return # 返回none 证明 可以执行 下面的request
user = session.get("user_info")
print(user)
if user:
return
return redirect("/") # 返回相对应的地址
@app.before_request
def process_request1(*args, **kwargs):
print("process_request1")
@app.before_request
def process_request2(*args, **kwargs):
print("process_request2")
@app.route("/", methods=["GET", "POST"], endpoint="login")
def login():
if request.method == "GET":
return render_template("login.html", )
else:
user = request.form.get("user")
pwd = request.form.get("pwd")
if user == "alex" and pwd == "123":
session["user_info"] = user
return redirect("/index")
else:
return render_template("login.html", **{"msg": "erro"})
@app.route("/index", methods=["GET"])
def index():
return render_template("index.html", user_dict=USERS)
@app.route("/detail/<int:nid>", methods=["GET"])
def detail(nid):
user_info = USERS.get(nid)
return render_template("detail.html", user_info=user_info)
@app.after_request
def process_response1(response, *args, **kwargs):
print("process_response1")
return response
@app.after_request
def process_response2(response, *args, **kwargs):
print("process_response2")
return response
# before_request返回none时 执行下一个request 否则拦截 但是还是会执行response函数
# 执行顺序 request1 -> request2 -> response2 -> response1
@app.errorhandler(404) # 定制错误信息
def erro_404(arg):
return "404"
if __name__ == '__main__':
app.run(port=5002)
9.2中间件的 自定制
# _*_ coding:utf-8 _*_
# __author__Zj__
from flask import Flask
app = Flask(__name__)
app.debug = True
# 自定义中间件
@app.route("/")
def sayhello():
return "hello word"
class Md(object):
def __init__(self, old_wsgi_app):
self.old_wsgi_app = old_wsgi_app
def __call__(self, environ, start_response):
# 实例化的时候运行call 方法
print("before")
ret = self.old_wsgi_app(environ, start_response)
print("after")
return ret
if __name__ == '__main__':
# 这一步 是进行实例化把
Flask——>app.wsgi_app
传递进来,接着就执行call方法 其实就是执行app.wsgi_app(参数)
app.wsgi_app = Md(app.wsgi_app) # 执行 Md __call__ 方法
app.run(port=9000)
app 的 call 方法源码
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ) # requestCountext
...........
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)