学习Flask的一些基础知识总结
Flask是个轻量级的web框架,可扩展性强,有大量第三方组件,最终可以实现类似Django这种大而全的重量级框架的功能。区别比较大的一点是;
在Django中request是在内部不断的进行封装和传递,而在Flask中则是基于上下文管理,把它放在一个地方,当需要用时去那里取即可。
Flask是基于werkzug实现wsgi协议的:
当我们不使用flask而仅仅使用werkzug来实现响应请求:
from werkzeug.serving import run_simple
def func(environ, start_response):
print('请求来了')
if __name__ == '__main__':
run_simple('127.0.0.1', 5000, func)
当访问127.0.0.1:5000时即可看到console打印出的请求来了,当然因为没有返回正常的response,而报错。
配置:
flask中的配置文件是一个flask.config.Config对象(继承字典)
# 方式一:
def from_envvar(self, variable_name, silent=False):
rv = os.environ.get(variable_name)
if not rv:
if silent:
return False
raise RuntimeError(
"The environment variable %r is not set "
"and as such configuration could not be "
"loaded. Set this variable and make it "
"point to a configuration file" % variable_name
)
return self.from_pyfile(rv, silent=silent)
从环境变量中获取,事实上这个变量是一个配置文件,它最后是调用from_pyfile去文件中获取变量。
#方式二
def from_pyfile(self, filename, silent=False):
filename = os.path.join(self.root_path, filename)
d = types.ModuleType("config")
d.__file__ = filename
try:
with open(filename, mode="rb") as config_file:
exec(compile(config_file.read(), filename, "exec"), d.__dict__)
except IOError as e:
if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR):
return False
e.strerror = "Unable to load configuration file (%s)" % e.strerror
raise
self.from_object(d)
return True
从一个py文件中获取,最后调用的是from_object方法 。
方式三:
#app.from_objects()
def from_object(self, obj):
if isinstance(obj, string_types):
obj = import_string(obj)
for key in dir(obj):
if key.isupper():
self[key] = getattr(obj, key)
object可以是字符串,通过import_string导入。也可以是一个对象,直接获取对象的属性。
#方式四
def from_json(self, filename, silent=False):
filename = os.path.join(self.root_path, filename)
try:
with open(filename) as json_file:
obj = json.loads(json_file.read())
except IOError as e:
if silent and e.errno in (errno.ENOENT, errno.EISDIR):
return False
e.strerror = "Unable to load configuration file (%s)" % e.strerror
raise
return self.from_mapping(obj)
从json文件中获取,json文件中本质是一个字典,所以最后调用方式五from_mapping
#方式五:
def from_mapping(self, *mapping, **kwargs):
mappings = []
if len(mapping) == 1:
if hasattr(mapping[0], "items"):
mappings.append(mapping[0].items())
else:
mappings.append(mapping[0])
elif len(mapping) > 1:
raise TypeError(
"expected at most 1 positional argument, got %d" % len(mapping)
)
mappings.append(kwargs.items())
for mapping in mappings:
for (key, value) in mapping:
if key.isupper():
self[key] = value
return True
从键值对中获取配置,不是大写的配置将被忽略掉。
常用的两种方式:
基于文件的方式:
在本地设置localsettings, prodsettings.py, 在prodsettings中加载localsettings中的内容,如果没有就什么也不做
try:
from .localsettings import *
except ImportError:
pass
基于类的方式,即From_object
class BaseSettings(object):
x = '1'
class LocalSettings(BaseSettings):
Host = 'localhost'
class ProdSettings(BaseSettings):
Host = '127.0.0.1'
app.config.from_object('config.settings.LocalSettings')
Flask路由:
路由加载流程
将url,函数打包成rule对象
将rule对象添加到map对象中
map对象封装在app.url_map = map对象
方式一:
def index():
return render_template('index.html')
app.add_url_rule('/index', 'index', index)
最后面的index取了视图函数的名称
方式二:推荐的方式
@app.route('/login',methods=['GET','POST'],endpoint="login")
def login():
pass
这里的“/login'即是路由的写法,且前面的/不能省略, methods则定义了允许的请求方法, endpoint是别名,类似于django中的name.与django中相同的是,endpoint也就是别名,不能存在两个相同的别名,否则不知道怎么分发路由而报错。
动态路由:
@app.route('/index/<name>')
def login(name):
pass
@app.route('/index/<int:nid>')
def login(nid):
pass
看第一个login , <name>即是传递的关键字参数,默认情况下这个参数是字符串类型,第二个视图中指定了int类型,这样我们获取到的参数就是Int类型,那么我们如何获取参数呢?
获取提交的数据:
from flask import request
@app.route('/index')
def login():
request.args # GET形式传递的参数
request.form # POST形式提交的参数
get携带的参数保存在request.args中,而表单提交的数据则保存在request.form中
返回数据:
from flask import render_template, jsonify,redirect
@app.route('/index')
def login():
return render_template('模板文件')
return jsonify()
reutrn redirect('/index/') # reutrn redirect(url_for('idx'))
render_template相当于django中的render, jsonify相当于django中的JsonResponse, redirect则与django相同,重定向。
redirect的参数可以是路由,也可以是别名,但如果写别名,则需要写成url_for()这样。
视图:
FBV
def index():
return render_template('index.html')
app.add_url_rule('/index', 'index', index)
# 公司里一般用这种方式
@app.route('/login')
def login():
return render_template('login.html')
CBV
def test1(func):
def inner(*args, **kwargs):
print('before1')
result = func(*args, **kwargs)
print('after1')
return result
return inner
def test2(func):
def inner(*args, **kwargs):
print('before2')
result = func(*args, **kwargs)
print('after2')
return result
return inner
class UserView(views.MethodView):
methods = ['GET',"POST"]
decorators = [test1,test2]
def get(self):
print('get')
return 'get'
def post(self):
print('post')
return 'post'
app.add_url_rule('/user', view_func=UserView.as_view('user')) # endpoint
CBV只能用add_url_rule的方式注册路由,其中as_view中的参数user即是endpoint
视图返回数据了在前端要怎么渲染呢?
模板渲染:
flask中默认使用jinja2浸染引擎,它比django默认的引擎更灵活,在模板中Jinjia2支持block, include,extends等django中支持的语法,但也有些不同:
变量 {{name}}与django相同
{% for key, value in data_dict.items() %}
<tr>
<td>{{ key }}</td>
<td>{{ value.name }}</td>
<td>{{ value.age }}</td>
<td>
<a href="/edit?id={{ key }}">编辑</a>
<a href="/delete?id={{ key }}">删除</a>
</td>
</tr>
{% endfor %}
与Django的区别:
字典方法需要加括号:data_dict.itmes()
字典中的值有有三种方式:value.name, value['name'], value.get('name')
定义全局模板方法:
@app.template_global() # {{ func("海宇") }}
def func(arg):
return '海子' + arg
这样在每个视图中都可以调用,注意它需要带括号
{% block content %}
<h1>MD</h1>
{% include 'form.html' %}
{{ func("汪洋") }}
{% endblock %}
@app.template_filter() # {{ "海宇"|x1("宇") }}
def x1(arg,name):
return '狗子' + arg + name
注意filter是在|在后面
app装饰的全局可以用,在蓝图中注册只能蓝图中注册的视图函数可用
特殊的装饰器:
@app.before_request
def f1():
if request.path == '/login':
return
print('f1')
@app.after_request
def f10(response):
print('f10')
return response
看看它的执行顺序:
from flask import Flask,render_template,request
app = Flask(__name__)
@app.before_request
def f1():
print('f1')
@app.before_request
def f2():
print('f2')
@app.after_request
def f10(response):
print('f10')
return response
@app.after_request
def f20(response):
print('f20')
return response
@app.route('/index')
def index():
print('index')
return render_template('index.html')
if __name__ == '__main__':
app.run()
app.__call__
注:这里的装饰器可以这样写:
def x1():
print('xxx')
app.before_request(x1)
输出结果:
f1
f2
index
127.0.0.1 - - [21/Nov/2019 16:16:25] "GET /index HTTP/1.1" 200 -
f20
f10
它的顺序是在before_request按加载顺序,而after_request则将加载顺序反转。