Flask类视图
Posted Flask学习笔记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask类视图相关的知识,希望对你有一定的参考价值。
类视图和add_url_rule
1.视图2.标准类视图1.app.add_rul_rule()
与视图关系2.使用类继承3.方法提示3.基于调度的类视图4.装饰器视图(函数装饰器/类装饰器)
类视图和add_url_rule
1.视图
当一个url请求进入后台时,一般有两种方式来进行处理:视图函数和类视图.视图函数直接使用一个函数来进行处理并返回数据给浏览器.类视图则是使用类来进行处理并返回的,所以当需要进行的处理比较简单,则可以考虑使用前者,处理比较复杂就考虑使用后者,但是最终还是需要看使用环境和需求而定.
类视图:支持继承,处理复杂的请求
2.标准类视图
定义:类需要继承
Flask.views.View
,并且重写dispatch_request
方法
dispatch_request
:当访问到对应的url时,就会实例化这个类,并执行dispatch_request
方法,并将这个方法的返回值返回给浏览器.dispatch_request
其实也是一个视图函数,所以它的返回值跟视图函数一样,必须是html模板\字符串\response对象\response对象的子类或者固定格式的元组类注册:
add_url_rule
方法进行注册,注册方式与视图函数基本一致,不过参数view_func
跟视图函数的使用有点区别,需要使用视图类的as_view方法进行转换,同时指定一个视图名称.
http://docs.jinkan.org/docs/flask/views.html
实现一个简单的类视图:
from flask import Flask,views,render_template
app=Flask(__name__)
app.config.update({'DEBUG':True, 'TEMPLATES_AUTO_RELOAD':True})
@app.route('/')
def index():
return 'index'
# 假设从数据库中载入大量的对象,并渲染到前端
users = [
{'admin':{
'id':1,
'passwd':'123'}
},
{'Jack': {
'id': 2,
'passwd': '123'}
}
]
class Get_User_Information(views.View):
def dispatch_request(self):
cont = {
'users':users
}
return render_template('users.html', **cont)
# 注册到网站
app.add_url_rule('/users/', endpoint='users', view_func=Get_User_Information.as_view('users'))
if __name__ == '__main__':
app.run()
继承了
flask.views.View
类,并复写了dispath_request()
方法
users.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Users</title>
</head>
<body>
{{ users }}
{{ url_for('users') }}
</body>
</html>
1.app.add_rul_rule()
与视图关系
语法:
def add_url_rule(
self,
rule,
endpoint=None,
view_func=None,
provide_automatic_options=None,
**options
):其中的
view_func
接受的是一个函数,并不是类,当使用的是类视图时,需要转化类到函数才能正常的接受使用.app.add_url_rule('/users/', endpoint='users', view_func=Get_User_Information.as_view('users'))
如上,使用了类的
as_view
方法,这是一个类方法,Flask
内部实现的,返回的对象是一个 函数对象@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) # 调用类本身的`dispath_request` 方法
if cls.decorators:
view.__name__ = name
view.__module__ = cls.__module__
for decorator in cls.decorators:
view = decorator(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
是其内部定义的一个函数,返回的是一个cls.dispatch_request()
对象.这是一个典型的闭包结构.类似与这样:
class My_Define(object):
info = {}
def get(self):
return '1'
@classmethod
def class_method(cls, name, *clsargs, **clskwargs):
def view(*args, **kwargs):
self = view.view_class(*clsargs, **clskwargs)
return self.get(*args, **kwargs)
view.view_class = cls
view.__name__ = name
cls.info['cls'] = cls.__name__
return view
class My_Define_sub(My_Define):
def get(self):
return self.__class__
print(My_Define_sub.__dict__)
My_Define_sub.class_method('test')
print(My_Define_sub.info)
print(type(My_Define_sub.class_method('test')))
print(My_Define_sub.class_method('test').__name__)
"""
{'__module__': '__main__', 'get': <function My_Define_sub.get at 0x7fd8baafed40>, '__doc__': None}
{'cls': 'My_Define_sub'}
<class 'function'>
test
"""可以看到它返回的对象确实是一个函数类型的对象.并且
cls
是子类本身.在这里
name
的值是必须的.当add_url_rule
中endpoint
没有定义的时候,url_for
要获取name
的值,这个定义在add_url_rule
中:def add_url_rule(
self,
rule,
endpoint=None,
view_func=None,
provide_automatic_options=None,
**options
):
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
# _endpoint_from_view_func(view_func)定义
def _endpoint_from_view_func(view_func):
assert view_func is not None, "expected view func if endpoint is not provided."
return view_func.__name__可以看到没有定义
endpoint
默认返回的是view_func.__name__
,即返回as_view('test').__name__
,这也就是必须填写as_view(name)
的原因.
2.使用类继承
大量的相同的内容,可以抽象成一个父类,然后在子类就可以用很少的代码完成工作.
from flask import Flask,views,render_template
app = Flask(__name__)
@app.route('/')
def index():
return '1'
ads={'ada1':'picture1', 'ads2':'picture2'}
class Page1_Views(views.View):
def dispatch_request(self):
cont = {
'ads':ads
}
return render_template('page1.html', **cont)
class Page2_views(views.View):
def dispatch_request(self):
cont = {
'ads':ads
}
return render_template('page2.html', **cont)
# 注册
app.add_url_rule('/page1/', endpoint('page1'), view_func=Page1_Views.as_view('page1'))
app.add_url_rule('/pages2/', endpoint('page2'), view_func=Page2_views.as_view('page2'))
# 大量的page都需要同样的数据
class Ads_view(views.View):
def get_templte_name(self):
raise NotImplementedError()
def render_tamplate_view(self, cont):
return render_template(self.get_tempalte_name(), **cont)
def dispatch_request(self):
cont = {
'ads':ads
}
return self.render_tamplate_view(**cont)
class Page_view(Ads_view):
def get_template_name(self):
return 'page3.html'
if __name__=='__main__':
app.run(debug=True)
3.方法提示
http请求有多种方式
get/post
,如果要在标准类视图中控制,需要添加methods
属性来控制class MyView(View):
methods = ['GET', 'POST']
def dispatch_request(self):
if request.method == 'POST':
...
...
app.add_url_rule('/myview', view_func=MyView.as_view('myview'))
3.基于调度的类视图
定义:类需要继承
Flask.views.MethodView
,并且定义对应的请求方法请求方法:当对应的url访问进来后就会根据url的请求方式查找对应的请求方法,并将该方法的返回值返回给浏览器,如果没有定义对应的请求方法就会报错,提示
Method Not Allowed
类注册:注册方式和标准类视图是相同的
一个简单的基于调度的类视图:
from flask import Flask,render_template,views,request
app = Flask(__name__)
@app.route('/')
def index():
return "Hello World"
class Login_View(views.MethodView):
def get(self):
return render_template('login.html')
def post(self):
# request.form.get 获取form 表单的数据,get()中是表单的name值
username = request.form.get('username')
passwd = request.form.get('password')
if username == 'admin' and passwd == '123456':
return "successful"
else:
return 'failed'
app.add_url_rule('/login/', view_func=Login_View.as_view('login'))
if __name__=='__main__':
app.run(debug=True)
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<div>
<form action="" method="post">
<label for="#">username</label>
<input type="text" name="username">
<label for="#">password</label>
<input type="password" name="password">
<label for="#">submit</label>
<input type="submit" name="submit">
</form>
</div>
</body>
</html>美化:
app.py
from flask import Flask,render_template,views,request
app = Flask(__name__)
@app.route('/')
def index():
return "Hello World"
class Login_View(views.MethodView):
def __render(self, error_info=None):
return render_template('login.html',error=error_info)
def get(self):
return self.__render()
def post(self):
# request.form.get 获取form 表单的数据,get()中是表单的name值
username = request.form.get('username')
passwd = request.form.get('password')
if username == 'admin' and passwd == '123456':
return "successful"
else:
return self.__render(error_info='failed')
app.add_url_rule('/login/', view_func=Login_View.as_view('login'))
if __name__=='__main__':
app.run(debug=True)
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<div>
<form action="" method="post">
<label for="#">username</label>
<input type="text" name="username">
<label for="#">password</label>
<input type="password" name="password">
<label for="#">submit</label>
<input type="submit" name="submit">
</form>
<p>
{% if error %}
<p style="color: red;">please try again</p>
{% endif %}
</p>
</div>
</body>
</html>
4.装饰器视图(函数装饰器/类装饰器)
函数装饰器:
在使用装饰器时,需要使用內建模块
functions.wraps(func)
来转化一下,原因是:import functools
def check_user_info(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@check_user_info
def my_check(name):
print(name)
def check_user_info1(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@check_user_info1
def my_check1(name):
print(name)
print(my_check.__name__)
print(my_check1.__name__)
"""
wrapper
my_check1
"""
# 可以看到不转换函数的 __name__ 和本身并不对应.from flask import Flask,request,render_template,views
import functools
app = Flask(__name__)
app.config.update({'DEBUG':True, 'TEMPLATES_AUTO_RELOAD':True})
def setting1(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
username = request.args.get('username')
password = request.args.get('password')
if username == 'admin' and password == '123456':
return 'hello'
else:
return func(*args, **kwargs)
return wrapper
@app.route('/')
def index():
return "Hello World"
@app.route('/setting1/')
@setting1
def setting1():
return 'function decorator'
if __name__=='__main__':
app.run()在类视图中使用装饰器
from flask import Flask,request,render_template,views
import functools
app = Flask(__name__)
app.config.update({'DEBUG':True, 'TEMPLATES_AUTO_RELOAD':True})
user_info = {
'admin':'123456'
}
# 定义一个函数装饰器.用于判断登陆的用户名,密码是否正确
# 这里需要应用一下 functions.wrapper 否则被装饰的函数的 __name__ 会改变
def check_user_info(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
username = request.form.get('username')
password = request.form.get('password')
for k,v in user_info.items():
if k == username and v == password:
# return func(*args, **kwargs)
return 'success'
else:
return 'please login.'
return wrapper
class Setting_View(views.MethodView):
def __rander(self):
return render_template('setting.html')
def get(self):
return self.__rander()
@check_user_info
def post(self):
return self.__rander()
app.add_url_rule('/setting/', view_func=Setting_View.as_view('setting'))
if __name__=='__main__':
app.run()
setting.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<form action="" method="post">
<label for="">username</label>
<input type="text" name='username'>
<label for="">password</label>
<input type="password" name='password'>
<label for="">submit</label>
<input type="submit" name='submit'>
</form>
</body>
</html>
以上是关于Flask类视图的主要内容,如果未能解决你的问题,请参考以下文章