Flask - 四剑客 | templates | 配置文件 | 路由系统 | CBV
Flask框架简介
说明:
flask是一个轻量级的web框架,被称为微型框架。只提供了一个高效稳定的核心,其它全部通过扩展来实现。意思就是你可以根据项目需要进行量身定制,也意味着你需要不断学习相关的扩展库。
核心:
WSGI系统、调试、路由
模板引擎(Jinja2,是flask核心开发者人员发开的)
安装:
>: pip install flask
werkzeug简介
Flask的socket是基于Werkzeug 实现的
Werkzeug是一个WSGI工具包,他可以作为一个Web框架的底层库。这里稍微说一下, werkzeug 不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等
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)
flask的使用
Flask运行的本质:
from flask import Flask # 实例化产生一个Flask对象 app = Flask(__name__) # 装饰器路由 @app.route('/') def index(): return 'ok' if __name__ == '__main__': # 本质是 run_simple(host, port, app, **options) # app() 调用的是Flask的__call__里的wsgi_app方法,返回response app.run()
启动参数
参数 说明
debug 是否开启调试模式,默认为False;开启后会有出错调试信息,文件会自动加载。
threaded 是否开启多线程,默认为Flase。
host 指定主机,默认为’127.0.0.1’,设置为’0.0.0.0’后可以通过IP进制访问
port 指定端口,默认为5000。
启动示例:app.run(debug=True, threaded=True, host=‘0.0.0.0’, port=5555)
响应四剑客 render_template,redirect,jsonify
-
返回字符串
@app.route('/') def index(): # 1 直接返回字符串 return 'ok'
-
返回HTML
from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): # 2 返回HTML return render_template('index.html') # 参数: xxx.HTML
-
跳转页面(重定向)
from flask import Flask, redirect app = Flask(__name__) @app.route('/') def index(): # 3 跳转页面 return redirect('/login') # 参数: url @app.route('/login') def login(): return render_template('login.html')
-
返回json数据
from flask import Flask, jsonify app = Flask(__name__) @app.route('/') def index(): # 4 返回json数据 dic = {'name':'xionger', 'age': 20} return jsonify(dic) # 参数: 要序列化的数据
templates(模板层)
1. 创建templates文件夹
2. 文件夹内创建HTML文件
-
视图函数向HTML页面传递数据
# 方式一
@app.route('/') def index(): dic = {'name': 'xionger', 'age': 20} return render_template('index.html', num=100, dic_data=dic)
# 方式二 from flask import Flask, render_template, Markup app = Flask(__name__) def func(t1, t2): return Markup(f'<h1>hahaha{t1}{t2}</h1>') @app.route('/list',methods=['GET']) def list(): return render_template('list.html',html="<h1>hahaha</h1>", func=func) # list.html {{ html|safe }} {{ func(1, 2) }}
-
模板语法
<body> <p>{{ num }}</p> 100 <p>{{ dic_data.name }}</p> xionger <p>{{ dic_data.get('name') }}</p> xionger <p>{{ dic_data['name'] }}</p> xionger </body> <!-- 字典, 列表等数据类型在python中如何使用, 在模板中就如何使用 -->
<!-- 遍历循环 --> <body> <h1>用户列表</h1> <table> {% for k,v in user_dict.items() %} <tr> <td>{{k}}</td> <td>{{v.name}}</td> <td>{{v['name']}}</td> <td>{{v.get('name')}}</td> <td><a href="/detail/{{k}}">查看详细</a></td> </tr> {% endfor %} </table> </body>
<!-- 逻辑判断 --> <body> <h1>用户列表</h1> <table> {% if name %} <h1>Hello {{ name }}!</h1> {% else %} <h1>Hello World!</h1> {% endif %} </table> </body>
配置文件
-
方式一
# 这中方式只能配置两种 app.debug=True app.secret_key="123123"
-
方式二 :使用config字典
app.config['DEBUG']=True
-
方式三: 导入文件(插拔式)
settings.py
# DEBUG = True 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
视图函数文件.py
app.config.from_object('settings.DevelopmentConfig')
补充:
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, }
路由系统
@setupmethod def add_url_rule( self, rule, # --> 装饰器里的路由 endpoint=None, # --> 路由别名 view_func=None, # --> 视图函数 provide_automatic_options=None, **options # 用来接收methods等参数的字典 ): if endpoint is None: # 若路由没有起别名 # endpoint = 视图函数的名字 endpoint = _endpoint_from_view_func(view_func) # 将endpoint添加到options里 options["endpoint"] = endpoint # 获取methods的值 methods = options.pop("methods", None) # if the methods are not given and the view_func object knows its # methods we can use that instead. If neither exists, we go with # a tuple of only ``GET`` as default. if methods is None: # methods为空 则默认是GET请求 methods = getattr(view_func, "methods", None) or ("GET",) if isinstance(methods, string_types): raise TypeError( "Allowed methods have to be iterables of strings, " 'for example: @app.route(..., methods=["POST"])' ) methods = set(item.upper() for item in methods) # Methods that should always be added required_methods = set(getattr(view_func, "required_methods", ())) # starting with Flask 0.8 the view_func object can disable and # force-enable the automatic options handling. if provide_automatic_options is None: provide_automatic_options = getattr( view_func, "provide_automatic_options", None ) if provide_automatic_options is None: if "OPTIONS" not in methods: provide_automatic_options = True required_methods.add("OPTIONS") else: provide_automatic_options = False # Add the required methods now. methods |= required_methods rule = self.url_rule_class(rule, methods=methods, **options) rule.provide_automatic_options = provide_automatic_options self.url_map.add(rule) if view_func is not None: old_func = self.view_functions.get(endpoint) # endpoint 是视图函数的名字 if old_func is not None and old_func != view_func: raise AssertionError( "View function mapping is overwriting an " "existing endpoint function: %s" % endpoint ) # 若view_functions中有endpoint 则取出赋值给view_func # view_func要么必须有值, 要么endpoint有别名, 最终endpoint的值也会赋值给view_func self.view_functions[endpoint] = view_func # 如果endpoint有别名 view_func = endpoint # 如果endpoint没有有别名 view_func = endpoint(视图函数名字)
-
典型装饰器写法
from flask import Flask app = Flask(__name__) # 路由装饰器配置该函数的路由 @app.route('/index/<int:nid>',methods=['GET'],endpoint='haha') def index(nid): return nid ''' '/index/<int:nid>' # 路由有名分组,视图函数要接收 methods # 该视图函数可以用的请求方式 endpoint # 路由别名反向解析, 在其他视图函数中用: real_url=url_for("别名") 调用该函数
endpoint默认指定的是函数名字 核心源码: route -> decorator -> add_url_rule flask路由基于装饰器,本质是基于:add_url_rule add_url_rule 源码中,endpoint如果为空, endpoint = _endpoint_from_view_func(view_func),最终取view_func.__name__(函数名) '''
-
add_url_rule
app.add_url_rule参数: - rule, URL规则 - view_func, 视图函数名称 - defaults = None, 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}为函数提供参数 - endpoint = None, 名称,用于反向生成URL,即: url_for('名称') - methods = None, 允许的请求方式,如:["GET", "POST"] - redirect_to = '/login' 重定向到指定的地址 可以是路径也可以是别名 - strict_slashes 严格模式 @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
def index(nid): return nid app.add_url_rule('/index/<string:nid>',view_func=index,endpoint="haha",methods=['POST',"GET"])
补充: 别名使用
def login(): return 'login' app.add_url_rule('/login',view_func=login, endpoint="haha", methods=['POST',"GET"]) def index(nid): real_url = url_for('haha')
print(real_url) # /login return real_url app.add_url_rule('/index/<string:nid>',view_func=index,methods=['POST',"GET"])
注意: 别名不能重复,一个路由一个别名
-
支持正则
1 写类,继承BaseConverter 2 注册:app.url_map.converters['regex'] = RegexConverter 3 使用:@app.route('/index/<regex("\d+"):nid>') 正则表达式会当作第二个参数传递到类中
from flask import Flask, url_for app = Flask(__name__) from werkzeug.routing import BaseConverter class RegexConverter(BaseConverter): """ 自定义URL匹配正则表达式 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 路由匹配时,匹配成功后传递给视图函数中参数的值 """ #value就正则匹配出来的结果 # print('value',value,type(value)) return value def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 """ val = super(RegexConverter, self).to_url(value) # print(val) # 666 return val app.url_map.converters['regex'] = RegexConverter @app.route('/test/<regex("\d+"):nid>',endpoint="tes") def test(nid): print("nid",nid,type(nid)) print(url_for('tes', nid='666')) # /test/666 return 'test'
CBV
as_view 源码
@classmethod def as_view(cls, name, *class_args, **class_kwargs): # cls = 视图类 name一定要传值 def view(*args, **kwargs): # view_class = cls 视图类 self = view.view_class(*class_args, **class_kwargs) # self 是视图类对象 # self调用dispatch_request方法,调用的是视图类中的dispatch_request方法 return self.dispatch_request(*args, **kwargs) if cls.decorators: view.__name__ = name view.__module__ = cls.__module__ for decorator in cls.decorators: view = decorator(view) # 在 view的名称空间中添加下面的键值对 view.view_class = cls view.__name__ = name view.__doc__ = cls.__doc__ view.__module__ = cls.__module__ view.methods = cls.methods view.provide_automatic_options = cls.provide_automatic_options return view
as_view -> view(View中的) -> dispatch_request(视图类中的)
-
普通使用
from flask import Flask, views app = Flask(__name__) class IndexView(views.View): methods = ['POST', 'GET'] # decorators = [ ] def dispatch_request(self): print('Index') return 'Index' app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) ''' methods = [] 视图类的请求方式, 默认GET, 若指定了请求方式,就只能用指定的方式请求该视图类 as_view(name='index') 要指定name = 别名, 若不指定,导致所有的路由都是view,别名一样报错 '''
MethodView 源码
class MethodView(with_metaclass(MethodViewType, View)): # 重写dispatch_request方法 def dispatch_request(self, *args, **kwargs): # self是视图类的对象 # 从self中获取request中的请求方式,并改成小写 meth = getattr(self, request.method.lower(), None) # 核心 使前端请求方式与视图类中的请求方式对应 # If the request method is HEAD and we don't have a handler for it # retry with GET. # 如果 meth是空并且请求方式是HEAD if meth is None and request.method == "HEAD": # 从self中获取get方法 meth = getattr(self, "get", None) # meth为空抛出错误 assert meth is not None, "Unimplemented method %r" % request.method # 当前端的请求方式在视图类中有时, # meth不为空,并调用meth映射的请求方式,返回 # 否则meth为空抛出异常 return meth(*args, **kwargs)
as_view -> view(View中的) -> dispatch_request(MethodView中的)
-
指定post / get
class LoginView(views.MethodView): # methods = ['GET'] # 默认支持 GET/POST ,若指定请求方式,该视图类只能用指定的方式 def get(self): print('get') return 'get' def post(self): print('post') return 'post' app.add_url_rule('/login', view_func=LoginView.as_view(name='login'))