Flask基础
初识Flask
Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
下载:
pip3 install flask
当前版本:1.1.2
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)
基本使用
一.FlaskDemo
from flask import Flask # 实例化一个Flask对象 给它指定一个名字 app = Flask(__name__) @app.route("/index") def index(): return "hello~~" if __name__ == '__main__': app.run()
二.配置文件
我们可以在实例化Flask对象之后~打印一下app.config来查看Flask的配置信息。
我们也可以通过app.config.xxx = xxx来更改配置信息,但是通常我们不这样去做~~
配置文件的实现方式:
先看一个应用小Demo
from flask import Flask
app = Flask(__name__)
app.config.from_object('settings.DevelopmentConfig')
@app.route('/index', methods=['GET', 'POST'])
def index():
return 'hello world'
if __name__ == '__main__':
app.run()
settings.py:
class BaseConfig(object): DEBUG = True SECRET_KEY = 'MAOMAO1019' class ProductionConfig(BaseConfig): DEBUG = False class DevelopmentConfig(BaseConfig): pass class TestingConfig(BaseConfig): pass
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为: { 'DEBUG': get_debug_flag(default=False), 是否开启Debug模式 'TESTING': False, 是否开启测试模式 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 开启session前需设置 '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.route('/user/<username>') @app.route('/post/<int:post_id>') @app.route('/post/<float:post_id>') @app.route('/post/<path:path>') # 路由默认支持的参数 DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
注意:直接<username>,就代表接收一个字符串类型的参数。
路由的几种写法:
def index(): return "Index" self.add_url_rule(rule='/index', endpoint="index", view_func=index, methods=["GET","POST"]
# 或者这样 app.add_url_rule(rule='/index', 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
其实,app.route()是通过装饰器,去执行add_url_rule(rule, endpoint, f, **options)这个方法。
@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()
除了flask给我们提供的正则路由匹配规则外,还可以自定义匹配规:
from flask import Flask, url_for from werkzeug.routing import BaseConverter app = Flask(__name__) app.config.from_object('settings.DevelopmentConfig') 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>', methods=['GET', 'POST'],endpoint='index') def index(nid): print(nid) print(type(nid)) # <class 'int'> print(url_for('index')) # /index/1 return 'hello 123' if __name__ == '__main__': app.run()
四.模板
Flask使用的是Jinja2模板,所以其语法和Django无差别
Flask中自定义模板方法的方式和Bottle相似,创建一个函数并通过参数的形式传入render_template:
from flask import Flask, render_template, Markup app = Flask(__name__) app.config.from_object('settings.DevelopmentConfig') # 这是一个全局的模板函数,可以在该APP下的所有函数中使用,注意:Markup等价django的mark_safe @app.template_global() def get_input(value): return Markup('<input type="%s">' % value) @app.route('/index', methods=['GET', 'POST']) def index(): context = { "k1": 123, "k2": [11, 22, 33], "k3": {"name": "oldboy", "age": 84}, "k4": lambda x: x + 1, } return render_template('index.html', **context) if __name__ == '__main__': app.run()
对应的index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>{{ k1 }}</h1> <h2>{{ k2[1] }},{{ k2.2 }}</h2> <h3>{{ k3.name }},{{ k3['name']}}, {{ k3.get('age', 888) }}</h3> <h3>{{ k4(1) }}</h3> <h3>{{ get_input('input') }}</h3> </body> </html>
五.请求和响应
常用的请求方法:
# 请求相关信息 # request.method # request.args # request.form # request.cookies # request.headers # request.path # request.files # obj = request.files['the_file_name'] # obj.save('/var/www/uploads/' + secure_filename(obj.filename)) # request.url # request.base_url # request.url_root # request.host_url # request.host # obj = request.files['the_file_name'] # obj.save('/var/www/uploads/' + secure_filename(f.filename))
常用的请求方法:
# 响应相关 # return 'hello world' # return template_rendered('index.html', n=123) # return jsonify({"name":"maomao"}) # 设置响应头 # response = make_response(render_template('index.html')) # response.set_cookie('key', 'value') # response.headers['X-Something'] = 'A value' # response.delete_cookie('key') # return response # response是flask.wrappers.Response类型
六.session
除了请求对象外,还有一个session对象,它允许你在不同请求间存储特定用户的信息。
它是在 Cookies 的基础上实现的,并且对 Cookies 进行加密,你需要设置一个密钥。
设置session:
session['xxxxxx'] = 'xxx'
删除session:
session.pop('xxxx', None)
基本使用:
from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__) app.secret_key = 'qwer666' @app.route('/') def index(): if 'username' in session: return "已经登录" return "未登录" @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] return redirect(url_for('index')) return render_template('login.html'i) @app.route('/logout') def logout(): session.pop('username', None) return redirect(url_for('index'))
七.特殊的装饰器
先回顾一下装饰器:
import functools def wrapper(func): @functools.wraps(func) def inner(*args, **kwargs): ret = func(*args, **kwargs) return ret return inner """ 1.执行wrapper函数,并将被装饰的函数当成参数。 wrapper(index) 2.将第一步的返回值,赋新值给index: 新index = wrapper(老index) """ @wrapper def index(a1): return a1 + 100 @wrapper def order(a1): return a1 + 100 print(index(200)) print(index.__name__) print(order.__name__)
flask给我们提供的装饰器,有点像Django中的中间件:before_request, after_request
from flask import Flask app = Flask(__name__) app.config.from_object('settings.DevelopmentConfig') @app.before_request def xxx1(): print('执行前1') @app.before_request def xxx2(): print('执行前2') @app.after_request def ooo1(response): print('执行后1') return response @app.after_request def ooo2(response): print('执行后2') return response @app.route('/x1', methods=['GET', 'POST']) def x1(): print('视图函数1') return 'hello x1' @app.route('/x2', methods=['GET', 'POST']) def x2(): print('视图函数2') return 'hello x2' if __name__ == '__main__': app.run()
我们访问http://127.0.0.1:5000/x1,打印出来的结果应该是:
执行前1
执行前2
视图函数1
执行后2
执行后1
其他的装饰器:
@app.template_global()
@app.template_filter()
@app.before_firse_request
@app.errorhandler(404)
通过before_request,在验证用户登录的时候,就不用在很多视图函数都用session去判断了:
@app.before_request def auth2(): if request.path == "/login": return None if session.get("userinfo"): return None return redirect("/login")
八.蓝图
我们写正式的项目时,可以通过蓝图,建立一个规范的目录结构。
蓝图的三大作用:
划分规范的目录结构
对URL进行划分
给某一个蓝图app加before_request
使用方法:
生成蓝图对象:
from flask imort Blueprint userBlue = Blueprint("userBlue", __name__) @userBlue.route("/user") def user(): return "USER"
注册蓝图对象:
from .view.user import userBlue from flask import Flask def create_app(): app = Flask(__name) app.register_blueprint(userBlue) return app
下面是一个小的Demo:
account.py:
from flask import Blueprint,render_template
ac = Blueprint('ac',__name__, url_prefix='/acc') # url_prefix给url加前缀
@ac.before_request
def bf():
print('before_request')
@ac.route('/login')
def login():
return render_template('login.html')
@ac.route('/logout')
def logout():
return 'logout'
admin.py:
from flask import Blueprint ad = Blueprint('ad',__name__) @ad.route('/home') def home(): return 'home'
user.py:
from flask import Blueprint us = Blueprint('us',__name__) @us.route('/info') def info(): return 'info'
init.py:
from flask import Flask app = Flask(__name__) app.debug = True from .views import account from .views import admin from .views import user app.register_blueprint(account.ac) app.register_blueprint(admin.ad) app.register_blueprint(user.us)
九.闪现
Flask 提供了一个非常简单的方法来使用闪现系统(Flash)向用户反馈信息。闪现系统使得在一个请求结束的时候记录一个信息,然后在且仅仅在下一个请求中访问这个数据。
from flask import Flask, session, flash, get_flashed_messages app = Flask(__name__) app.config.from_object('settings.DevelopmentConfig') @app.route('/x1', methods=['GET', 'POST']) def x1(): flash('我爱python1') # flash('我爱python2',category='x2') return 'hello x1' @app.route('/x2', methods=['GET', 'POST']) def x2(): data = get_flashed_messages() print(data) return 'hello x2' if __name__ == '__main__': app.run()