Flask简单使用、配置文件以及路由系统
Flask简介
Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
“微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。
默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用
wsgiref
最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口协议来实现这样的服务器软件,让我们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。而wsgiref模块就是python基于wsgi协议开发的服务模块
wsgiref简单应用
from wsgiref.simple_server import make_server def mya(environ, start_response): print(environ) start_response('200 OK', [('Content-Type', 'text/html')]) if environ.get('PATH_INFO') == '/index': with open('index.html','rb') as f: data=f.read() elif environ.get('PATH_INFO') == '/login': with open('login.html', 'rb') as f: data = f.read() else: data=b'<h1>Hello, web!</h1>' return [data] if __name__ == '__main__': myserver = make_server('', 8011, mya) print('监听8010') myserver.serve_forever()
wsgi源码流程
""" 1.程序启动,等待用户请求到来 app.run()->run_sample() 2.用户请求到来app()->app.__call__(),__call__返回什么客户就看到什么 根据源码可以看到最后是用的如下模块 from flask.wrappers import Response """
eg:
1.安装
pip3 install flask
2.werkzeug简介
Werkzeug是一个WSGI工具包,他可以作为一个Web框架的底层库。这里稍微说一下, werkzeug 不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等
代码示例:依赖wsgi werkzeug起一个web
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)
3.flask快速使用
from flask import Flask # 实例化产生一个Flask对象 app = Flask(__name__) # 将 '/'和视图函数hello_workd的对应关系添加到路由中 @app.route('/') # 1. v=app.route('/') 2. v(hello_world) def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run() # 最终调用了run_simple()
小结
# flask 框架是基于 werkzeug 的 Wsgi 实现,flask 自己没有 Wsgi。 # 用户请求一旦到来,就会之app.__call__方法 # 写flask标准流程;如上flask快速使用
flask对象参数
app = Flask(__name__, template_folder='templates', static_folder='static')
静态static目录使用示例
上图html文件中引入的两种方式
<img src="/yy/mm.jpg"/> # 写死的是static_url_path <img src="{{url_for('xx', filename='mm.jpg')}}"/> # 第一个参数是static_folder,如果static_url_path发生变化,也能动态匹配上
三板斧以及JsonResponse
from flask import Flask, request, render_template, redirect, jsonify app = Flask(__name__) # 解决jsonify返回中文问题 app.config['JSON_AS_ASCII'] = False # jsonify转变格式的时候不会转变为unicode编码格式,unicode编码格式无法直接看到汉字 app.config['JSONIFY_MIMETYPE'] = 'application/json;charset=utf-8' # 指定浏览器渲染的文件类型,和解码格式 @app.route('/') def index(): # return 'hello' # return render_template('index.html') # return redirect('http://www.baidu.com') return redirect('/hello') # return jsonify({'code': 200, 'message': '欢迎来到主页'}) @app.route('/hello') def hello(): return 'hello' ''' django flask HttpResponse---->直接字符串 render()----->render_template('index.html') redirect()---->redirect() JsonResponse ----> jsonify() ''' if __name__ == '__main__': app.run()
案例:登录,显示用户信息
目录:
普通版:
app.py
from flask import Flask, render_template, request, redirect, session, url_for, jsonify from werkzeug.routing import BaseConverter app = Flask(__name__) app.debug = True # 调试模式 app.secret_key = 'asdfsadfsdafasdfasdf' # 跟djangosetting中的秘钥一个意思 app.config['JSON_AS_ASCII'] = False # jsonify转变格式的时候不会转变为unicode编码格式,unicode编码格式无法直接看到汉字 app.config['JSONIFY_MIMETYPE'] = 'application/json;charset=utf-8' # 指定浏览器渲染的文件类型,和解码格式 class RegexConverter(BaseConverter): """ 自定义URL匹配正则表达式 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 路由匹配时,匹配成功后传递给视图函数中参数的值 """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 """ val = super(RegexConverter, self).to_url(value) return val # 添加到flask中 app.url_map.converters['regex'] = RegexConverter USERS = { 1: {'name': '张三', 'age': 18, 'gender': '男', 'text': "道路千万条"}, 2: {'name': '李四', 'age': 28, 'gender': '男', 'text': "安全第一条"}, 3: {'name': '王五', 'age': 18, 'gender': '女', 'text': "行车不规范"}, } @app.route('/') def hello_world(): # print(request.method) # return 'Hello World!' return jsonify({'code': 200, 'message': '欢迎来到主页'}) @app.route('/detail/<int:nid>', methods=['GET']) def detail(nid): user = session.get('user_info') if not user: return redirect('/login') info = USERS.get(nid) return render_template('detail.html', info=info) @app.route('/index', methods=['GET']) def index(): # 从session中拿出user_info user = session.get('user_info') if not user: # return redirect('/login') url = url_for('l1') # 反向解析 print(url) return redirect(url) return render_template('index.html', user_dict=USERS) @app.route('/edit', methods=['GET', 'POST']) def edit(): user = session.get('user_info') if not user: return redirect('/login') pk = int(request.args.get('pk')) if request.method == "GET": return render_template('edit.html', user_dict=USERS[pk]) name = request.form.get('name') age = request.form.get('age') gender = request.form.get('gender') text = request.form.get('text') edit_user = USERS[pk] edit_user['name'] = name edit_user['age'] = age edit_user['gender'] = gender edit_user['text'] = text return redirect('/index') @app.route('/delete/<regex("\d+"):pk>', methods=['GET']) def delete(pk): user = session.get('user_info') if not user: return redirect('/login') # print(pk) USERS.pop(pk) return redirect('/index') @app.route('/login', methods=['GET', 'POST'], endpoint='l1') # endpoint路由的别名,用作反向解析 def login(): if request.method == "GET": return render_template('login.html') else: # post提交过来的数据放在form中 # get请求提交过来的数据args中 user = request.form.get('user') pwd = request.form.get('pwd') if user == 'lqz' and pwd == '123': # 往session中放入key和value session['user_info'] = user return redirect('/index') return render_template('login.html', error='用户名或密码错误') def xxx(): user = session.get('user_info') if not user: return redirect('/login') return 'xxx' app.add_url_rule('/xxx', view_func=xxx) if __name__ == '__main__': app.run() ''' 学到的 1 @app.route('/login',methods=['GET','POST'],endpoint='l1'), -第一个参数是路径, -methods=['GET','POST'],请求方式 -endpoint='l1',别名,如果不写,使用函数名作为别名 路由的本质:self.add_url_rule '''
detail.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>详细信息 {{info.name}}</h1> <div> {{info.text}} </div> </body> </html>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>用户列表</h1> <table border="1"> <thead> <tr> <th>ID</th> <th>用户名</th> <th>年龄</th> <th>性别</th> <th>查看详情</th> <th>操作</th> </tr> </thead> <tbody> {% for key,value in user_dict.items() %} <tr> <td>{{key}}</td> <td>{{value.name}}</td> {# <td>{{v['name']}}</td>#} {# <td>{{v.get('name')}}</td>#} <td>{{value.age}}</td> <td>{{value.gender}}</td> <td><a href="/detail/{{k}}">查看详细</a></td> <td> <a href="/edit?pk={{ key }}">编辑</a> <a href="/delete/{{ key }}">删除</a> </td> </tr> {% endfor %} </tbody> </table> </body> </html>
edit.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>用户编辑</h1> <form method="post"> <input type="text" name="name" value="{{ user_dict.name }}"> <input type="text" name="age" value="{{ user_dict.age }}"> <input type="text" name="gender" value="{{ user_dict.gender }}"> <input type="text" name="text" value="{{ user_dict.text }}"> <input type="submit" value="修改"> <span style="color: red">{{error}}</span> </form> </body> </html>
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>用户登录</h1> <form method="post"> <input type="text" name="user"> <input type="text" name="pwd"> <input type="submit" value="登录"> <span style="color: red">{{error}}</span> </form> </body> </html>
使用装饰器版:(只变动app.py文件内容)
app.py
from flask import Flask, render_template, request, redirect, session, url_for, jsonify from werkzeug.routing import BaseConverter import functools app = Flask(__name__) app.debug = True # 调试模式 app.secret_key = 'asdfsadfsdafasdfasdf' # 跟djangosetting中的秘钥一个意思 app.config['JSON_AS_ASCII'] = False # jsonify转变格式的时候不会转变为unicode编码格式,unicode编码格式无法直接看到汉字 app.config['JSONIFY_MIMETYPE'] = 'application/json;charset=utf-8' # 指定浏览器渲染的文件类型,和解码格式 class RegexConverter(BaseConverter): """ 自定义URL匹配正则表达式 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 路由匹配时,匹配成功后传递给视图函数中参数的值 """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 """ val = super(RegexConverter, self).to_url(value) return val # 添加到flask中 app.url_map.converters['regex'] = RegexConverter USERS = { 1: {'name': '张三', 'age': 18, 'gender': '男', 'text': "道路千万条"}, 2: {'name': '李四', 'age': 28, 'gender': '男', 'text': "安全第一条"}, 3: {'name': '王五', 'age': 18, 'gender': '女', 'text': "行车不规范"}, } # 登录认证装饰器 def auth(func): @functools.wraps(func) # 加上这个打印函数.__name__ def inner(*args, **kwargs): user = session.get('user_info') if not user: return redirect('/login') res = func(*args, **kwargs) return res return inner @app.route('/') def hello_world(): # print(request.method) # return 'Hello World!' return jsonify({'code': 200, 'message': '欢迎来到主页'}) @app.route('/detail/<int:nid>', methods=['GET']) @auth def detail(nid): # user = session.get('user_info') # if not user: # return redirect('/login') info = USERS.get(nid) return render_template('detail.html', info=info) @app.route('/index', methods=['GET']) @auth def index(): # 从session中拿出user_info # user = session.get('user_info') # if not user: # # return redirect('/login') # url = url_for('l1') # 反向解析 # print(url) # return redirect(url) return render_template('index.html', user_dict=USERS) @app.route('/edit', methods=['GET', 'POST']) @auth def edit(): # user = session.get('user_info') # if not user: # return redirect('/login') pk = int(request.args.get('pk')) if request.method == "GET": return render_template('edit.html', user_dict=USERS[pk]) name = request.form.get('name') age = request.form.get('age') gender = request.form.get('gender') text = request.form.get('text') edit_user = USERS[pk] edit_user['name'] = name edit_user['age'] = age edit_user['gender'] = gender edit_user['text'] = text return redirect('/index') @app.route('/delete/<regex("\d+"):pk>', methods=['GET']) @auth def delete(pk): # user = session.get('user_info') # if not user: # return redirect('/login') # print(pk) USERS.pop(pk) return redirect('/index') @app.route('/login', methods=['GET', 'POST'], endpoint='l1') # endpoint路由的别名,用作反向解析 def login(): if request.method == "GET": return render_template('login.html') else: # post提交过来的数据放在form中 # get请求提交过来的数据args中 user = request.form.get('user') pwd = request.form.get('pwd') if user == 'lqz' and pwd == '123': # 往session中放入key和value session['user_info'] = user return redirect('/index') return render_template('login.html', error='用户名或密码错误') @auth def xxx(): # user = session.get('user_info') # if not user: # return redirect('/login') return 'xxx' app.add_url_rule('/xxx', view_func=xxx) if __name__ == '__main__': app.run() ''' 学到的 1 @app.route('/login',methods=['GET','POST'],endpoint='l1'), -第一个参数是路径, -methods=['GET','POST'],请求方式 -endpoint='l1',别名,如果不写,使用函数名作为别名 路由的本质:self.add_url_rule '''
flask使用装饰器注意事项:
1)装饰器内需要加上@functools.wraps(func) # 登录认证装饰器 def auth(func): @functools.wraps(func) # 加上这个打印函数.__name__ def inner(*args, **kwargs): user = session.get('user_info') if not user: return redirect('/login') res = func(*args, **kwargs) return res return inner 2)装饰器要写在route下 @app.route('/detail/<int:nid>', methods=['GET']) @auth def detail(nid): # user = session.get('user_info') # if not user: # return redirect('/login') info = USERS.get(nid) return render_template('detail.html', info=info)
@app.route和app.add_url_rule参数
@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()
4.配置文件
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(...)
方式二
#通过py文件配置 app.config.from_pyfile("python文件名称")
app.config.from_pyfile("settings") 如: settings.py DEBUG = True app.config.from_pyfile("settings.py") #通过环境变量配置 app.config.from_envvar("环境变量名称") #app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) 环境变量的值为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') # pro_flask.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目录(Flask对象init方法的参数)
4.1 具体用法
4.1.1 基于全局变量
manage.py
settings.py
localsettings.py
4.1.2 基于类的方式
5.路由系统
典型写法
@app.route('/detail/<int:nid>',methods=['GET'],endpoint='detail')
- @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, }
路由系统本质
""" 1. decorator = app.route('/',methods=['GET','POST'],endpoint='n1') def route(self, rule, **options): # app对象 # rule= / # options = {methods=['GET','POST'],endpoint='n1'} def decorator(f): endpoint = options.pop('endpoint', None) self.add_url_rule(rule, endpoint, f, **options) return f return decorator 2. @decorator decorator(index) """ #同理 def login(): return '登录' app.add_url_rule('/login', 'n2', login, methods=['GET',"POST"]) #与django路由类似 #django与flask路由:flask路由基于装饰器,本质是基于:add_url_rule #add_url_rule 源码中,endpoint如果为空,endpoint = _endpoint_from_view_func(view_func),最终取view_func.__name__(函数名)
app.add_url_rule参数
@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"] #对URL最后的 / 符号是否严格要求 strict_slashes = None ''' @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>') ''' #子域名访问 subdomain = None, ''' #C:\Windows\System32\drivers\etc\hosts 127.0.0.1 www.liuqingzheng.com 127.0.0.1 admin.liuqingzheng.com 127.0.0.1 buy.liuqingzheng.com from flask import Flask, views, url_for app = Flask(import_name=__name__) app.config['SERVER_NAME'] = 'liuqingzheng.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" #可以传入任意的字符串,如传入的字符串为aa,显示为 aa.liuqingzheng.com @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() 访问: http://www.liuqingzheng.com:5000/dynamic http://admin.liuqingzheng.com:5000/dynamic http://buy.liuqingzheng.com:5000/dynamic '''
支持正则
#1 写类,继承BaseConverter #2 注册:app.url_map.converters['regex'] = RegexConverter #3 使用:@app.route('/index/<regex("\d+"):nid>') 正则表达式会当作第二个参数传递到类中 from flask import Flask, views, 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): """ 路由匹配时,匹配成功后传递给视图函数中参数的值 """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 """ val = super(RegexConverter, self).to_url(value) return val # 添加到flask中 app.url_map.converters['regex'] = RegexConverter @app.route('/index/<regex("\d+"):nid>') def index(nid): print(url_for('index', nid='888')) return 'Index' if __name__ == '__main__': app.run()
6、视图
CBV示例:
from flask import Flask,request,render_template,redirect from flask import views app=Flask(__name__) # class IndexView(views.View): # methods = ['GET'] # # decorators = [auth, ] # def dispatch_request(self): # print('Index') # return 'Index!' def auth(func): def inner(*args, **kwargs): print('before') result = func(*args, **kwargs) print('after') return result return inner class IndexView(views.MethodView): methods = ['GET'] # 指定运行的请求方法 # 登录认证装饰器加在哪? decorators = [auth, ] #加多个就是从上往下的效果 def get(self): print('xxxxx') return "我是get请求" def post(self): return '我是post请求' # 路由如何注册? # IndexView.as_view('index'),必须传name app.add_url_rule('/index',view_func=IndexView.as_view('index')) if __name__ == '__main__': app.run()
CBV总结
# flask的视图通常使用fbv,但是也支持使用cbv # 具体使用 - 继承 View类,书写get方法或post等方法,但是需要重写 class LoginView(views.View): def dispatch_request(self): pass - 继承MethodView(内部继承View),不需要在重写dispatch_request方法 class LoginView(views.MethodView): def get(self): pass - 路由 app.add_url_rule('/', view_func=LoginView.as_view(name='login')) - as_view内部也类似django,是一个闭包 - dispatch_request()方法和django中cbv的dispatch方法及其类似,使用反射,做分发工作 - 指定允许的请求方式:类属性 methods = ['GET'] - 使用的装饰器:类属性 decorators = [装饰器名字,] # 从左往右依次执行 - as_view方法内装饰起源码源码 if cls.decorators: view.__name__ = name view.__module__ = cls.__module__ for decorator in cls.decorators: view = decorator(view)
CBV(源码分析)
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 #或者,通常用此方式 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