什么是CBV
CBV在Django中也是接触过的
FBV就是function CBV就是class
FBV简单, 小巧, 当不涉及到复杂的逻辑时可以使用FBV
CBV 灵活, 类的封装, 继承, 多态。
在Flask中实现CBV
from flask import Flask, views, render_template, request app = Flask(__name__) class Login(views.MethodView): def get(self): return render_template('login.html') def post(self): return request.form.get('username') app.add_url_rule("/login", endpoint=None, view_func=Login.as_view(name="login")) if __name__ == '__main__': app.run("0.0.0.0",9999)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>登录</h1> <form action="" method="post"> <input type="text" name="username"> <input type="submit"> </form> </body> </html>
CBV有了, 路由在哪里, 之前FBV都是使用装饰器来声明一个路由的,那么CBV是怎么实现的呢。
其实只要看一看@app.route()的源码就会知道该怎么做了。
@app.route()源码浅解
class Flask(_PackageBoundObject): def route(self, rule, **options): def decorator(f): """ :param f: 视图函数 :return: 内部函数 decorator """ endpoint = options.pop('endpoint', None) # 从参数中弹出endpoint, 没有的话就是None self.add_url_rule(rule, endpoint, f, **options) # 调用这个函数, 将路由规则, endpoint, 视图函数传了进去, 其实这里就是去添加路由和视图的对应关系了 return f return decorator
点进去add_url_rule,查看是怎么添加路由和视图的对应关系的
@setupmethod def add_url_rule(self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options): """ :param rule: 视图函数的route装饰器中定义的路由规则 :param endpoint: 从option中取出的endpoint :param view_func: 视图函数 :param provide_automatic_options: :param options: :return: """ if endpoint is None: # endpoint如果为None endpoint = _endpoint_from_view_func(view_func) # 将试图视图函数传了进去, 返回视图函数的__name__ options['endpoint'] = endpoint # 重新赋值endpoint, 这时endpoint有可能等于视图函数的__name__, 或还是之前的值 methods = options.pop('methods', None) # 将请求方式弹出 if methods is None: 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) required_methods = set(getattr(view_func, 'required_methods', ())) 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 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) if old_func is not None and old_func != view_func: raise AssertionError('View function mapping is overwriting an ' 'existing endpoint function: %s' % endpoint) self.view_functions[endpoint] = view_func # 重要的一句, 看样是是字典增加键值对的操作, key: endpoint: value: func
打印下view_function
from flask import Flask, views, render_template, request app = Flask(__name__) print(app.view_functions) class Login(views.MethodView): def get(self): return render_template('login.html') def post(self): return request.form.get('username') app.add_url_rule("/login", endpoint=None, view_func=Login.as_view(name="login")) print(app.view_functions) if __name__ == '__main__': app.run("0.0.0.0", 9999) # 结果: {'static': <bound method _PackageBoundObject.send_static_file of <Flask 's1'>>} {'static': <bound method _PackageBoundObject.send_static_file of <Flask 's1'>>, 'login': <function View.as_view.<locals>.view at 0x00000188DEA61E18>}
上面代码中也写出了正确的CBV写法,就是通过这一行代码:
app.add_url_rule("/login", endpoint=None, view_func=Login.as_view(name="login"))
@app.route()调用add_url_rule()来添加对应关系,我们也可以自己调用add_url_rule()。
继承关系,写的Login继承的是 views.MethodView,MethodView又继承 MethodViewType, View ,在class View中有个as_view方法
view_func就等于Login.as_view(name='login')的执行结果
as_view()中的name参数是必须要传的, name是用来填充endpoint的,如果endpoint有值的话,name就没用,没有值的话name就是endpoint。
CBV后面也要执行as_view()这和django一样, 甚至连内部的逻辑都是大致相同, 执行完as_view()后return出一个函数, 这个函数会获取对应请求方式的函数, 也就是执行和请求方式对应的函数
简单看下as_view()
@classmethod def as_view(cls, name, *class_args, **class_kwargs): def view(*args, **kwargs): self = view.view_class(*class_args, **class_kwargs) 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就是上面的函数,cls就是类Login,name就是login 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 # 将内部函数view返回
再来看看class MethodViewType
class MethodViewType(type): def __init__(cls, name, bases, d): super(MethodViewType, cls).__init__(name, bases, d) # d是一个字典,如果自己没有定义methods if "methods" not in d: methods = set() for base in bases: if getattr(base, "methods", None): methods.update(base.methods) for key in http_method_funcs: if hasattr(cls, key): methods.add(key.upper()) if methods: cls.methods = methods
然后走视图函数:
def dispatch_request(self, *args, **kwargs): meth = getattr(self, request.method.lower(), None) # 拿到request.method.lower(),比如有get之类的。 # self就是实例化的login对象 # If the request method is HEAD and we don't have a handler for it # retry with GET. if meth is None and request.method == "HEAD": meth = getattr(self, "get", None) assert meth is not None, "Unimplemented method %r" % request.method return meth(*args, **kwargs) # 给meth加括号返回了
HTTP 请求方法
根据 HTTP 标准,HTTP 请求可以使用多种请求方法。
HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法。
HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。
常用的8中,在源码中也有:
具体的解释:
序号 | 方法 | 描述 |
1 | GET | 请求指定的页面信息,并返回实体主体。 |
2 | POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 |
3 | HEAD | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
4 | OPTIONS | 允许客户端查看服务器的性能。 |
5 | DELETE | 请求服务器删除指定的页面。 |
6 | PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
7 | TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
8 | PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 。 |
Flask-Session
官方认可的第三方组件
需要安装:pip3 install flask-session
from flask import Flask, session from flask_session import Session app = Flask(__name__) Session(app) @app.route('/login') def login(): session["username"] = 200 return '200 ok' if __name__ == '__main__': app.run('0.0.0.0', 9999)
redis和python
Windows下载redis
https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100
正常安装后配置好环境变量
redis-server redis-cli set k 设置 get k 取值
select 0-15 默认是0,最多到15,用于数据隔离,避免重复
python中的用法:
from redis import Redis r = Redis(host='127.0.0.1', port=6379, db=6) r.set("name", '456') print(r.get('name'))
Flask中redis和session
from flask import Flask, session from flask_session import Session from redis import Redis app = Flask(__name__) app.config['SESSION_TYPE'] = "redis" app.config['SESSION_REDIS'] = Redis(host="127.0.0.1", port=6379, db=6) Session(app) @app.route('/login') def login(): session["username"] = 200 return '200 ok' if __name__ == '__main__': app.run('0.0.0.0', 9999)