为可扩展功能子类化 Flask 可插入视图的最佳方法
Best way to subclass Flask pluggable views for extensible functionality
我正在构建一个网络应用程序,其中不同的视图将具有不同数量的 'wrapped functionality'(例如身份验证、logging/error 处理、数据库访问等),并且能够轻松地在视图之间共享此功能.
我认为可插入视图将是处理此问题的好方法,它通过重复子类化视图来构建包含视图主要操作的功能层。
然而,我正在努力找出实现它的最佳方法。我正在考虑链接装饰器,但继承似乎效果不佳。
例如,带有一些自定义日志记录和错误处理的简化视图:
from flask.views import View
class LoggedView(View):
def __init__(self,template):
self.template=template
#Decorator method for error handling and logging
def log_view(self,view):
def decorator(**kwargs):
try:
#Set up custom logging
self.log = .....
#Execute view
return view(**kwargs)
except CustomApplicationError as e:
#Log and display error
self.log.error(e)
return render_template('error.html',error=str(e))
return decorator
decorators=[log_view]
#This can be overridden for more complex views
def dispatch_request(self):
return render_template(self.template)
视图可以像这样使用:
app.add_url_rule('/index', view_func=LoggedView.as_view('index',template='index.html'))
然后如果我想在此视图的基础上添加用户身份验证:
class RestrictedView(LoggedView):
#Decorator method for user access validation
def validate_access(self,view):
def decorator(**kwargs):
g.user=session.get('user')
if g.user is None:
return redirect(url_for('login'))
#Execute view
return view(**kwargs)
return decorator
#How to add this functionality to the decorator chain? e.g. I dont think this works:
decorators.append(validate_access)
然后我想重复这个子类化以添加更多功能,例如数据库访问
- 有没有更好的方法来实现我想要做的事情?
- 将装饰器作为视图方法有意义吗?在装饰器中使用 'self' 是否有效?
如有任何建议,我们将不胜感激!
decorators
是一个列表,一个可变结构。您不能只在 subclass 中附加它。名称 decorators
未在子 class 中定义,如果您追加到 LoggedView.decorators
,您将追加到错误的列表!
您必须在子 class 中创建一个 new 列表对象以屏蔽基础 class 上的属性;您可以通过连接到基本 class 序列来构造一个;我在这里使用元组来使这一点更清楚:
class LoggedView(View):
decorators = (log_view,)
class RestrictedView(LoggedView):
decorators = LoggedView.decorators + (validate_access,)
请注意,装饰器不是方法,它们在应用时不绑定到视图,因此没有 self
参数。
如果您需要从装饰器访问视图实例,那么不要使用View.decorators
,这些装饰器会装饰一个简单的函数,调用时会先创建视图在该视图上调用 View.dispatch_request()
;当您调用 View.as_view()
时,返回的就是这个简单的函数。另一方面,如果您需要能够访问装饰器在注册路由时或(在另一个方向上)查找端点的注册视图时生成的包装器,那么使用 View.decorators
是完全正确的。
您可以直接修饰方法(包括 dispatch_request()
)或在 dispatch_request()
:
中实现您自己的机制
import inspect
class LoggedView(View):
method_decorators = (log_view,)
#This can be overridden for more complex views
def dispatch_request(self):
# decorate methods
cls = type(self)
members = vars(type(self)).items()
for name, object in members:
if not inspect.isfunction(object):
continue
if name == 'dispatch_request':
continue
# add bound decorated functions to the view
for d in self.method_decorators:
setattr(self, name, d(object).__get__(self, cls))
# dispatch
return render_template(self.template)
这是 Flask-RESTFul 项目用于允许在一行中为视图上的所有方法指定装饰器的路径。
然后从包装器调用参数中提取 self
参数(但将其传递给包装函数):
def log_view(view):
def decorator(self, **kwargs):
try:
#Set up custom logging
self.log = .....
#Execute view
return view(self, **kwargs)
except CustomApplicationError as e:
#Log and display error
self.log.error(e)
return render_template('error.html',error=str(e))
return decorator
我会在视图classes.
外部定义装饰器函数
我正在构建一个网络应用程序,其中不同的视图将具有不同数量的 'wrapped functionality'(例如身份验证、logging/error 处理、数据库访问等),并且能够轻松地在视图之间共享此功能.
我认为可插入视图将是处理此问题的好方法,它通过重复子类化视图来构建包含视图主要操作的功能层。
然而,我正在努力找出实现它的最佳方法。我正在考虑链接装饰器,但继承似乎效果不佳。
例如,带有一些自定义日志记录和错误处理的简化视图:
from flask.views import View
class LoggedView(View):
def __init__(self,template):
self.template=template
#Decorator method for error handling and logging
def log_view(self,view):
def decorator(**kwargs):
try:
#Set up custom logging
self.log = .....
#Execute view
return view(**kwargs)
except CustomApplicationError as e:
#Log and display error
self.log.error(e)
return render_template('error.html',error=str(e))
return decorator
decorators=[log_view]
#This can be overridden for more complex views
def dispatch_request(self):
return render_template(self.template)
视图可以像这样使用:
app.add_url_rule('/index', view_func=LoggedView.as_view('index',template='index.html'))
然后如果我想在此视图的基础上添加用户身份验证:
class RestrictedView(LoggedView):
#Decorator method for user access validation
def validate_access(self,view):
def decorator(**kwargs):
g.user=session.get('user')
if g.user is None:
return redirect(url_for('login'))
#Execute view
return view(**kwargs)
return decorator
#How to add this functionality to the decorator chain? e.g. I dont think this works:
decorators.append(validate_access)
然后我想重复这个子类化以添加更多功能,例如数据库访问
- 有没有更好的方法来实现我想要做的事情?
- 将装饰器作为视图方法有意义吗?在装饰器中使用 'self' 是否有效?
如有任何建议,我们将不胜感激!
decorators
是一个列表,一个可变结构。您不能只在 subclass 中附加它。名称 decorators
未在子 class 中定义,如果您追加到 LoggedView.decorators
,您将追加到错误的列表!
您必须在子 class 中创建一个 new 列表对象以屏蔽基础 class 上的属性;您可以通过连接到基本 class 序列来构造一个;我在这里使用元组来使这一点更清楚:
class LoggedView(View):
decorators = (log_view,)
class RestrictedView(LoggedView):
decorators = LoggedView.decorators + (validate_access,)
请注意,装饰器不是方法,它们在应用时不绑定到视图,因此没有 self
参数。
如果您需要从装饰器访问视图实例,那么不要使用View.decorators
,这些装饰器会装饰一个简单的函数,调用时会先创建视图在该视图上调用 View.dispatch_request()
;当您调用 View.as_view()
时,返回的就是这个简单的函数。另一方面,如果您需要能够访问装饰器在注册路由时或(在另一个方向上)查找端点的注册视图时生成的包装器,那么使用 View.decorators
是完全正确的。
您可以直接修饰方法(包括 dispatch_request()
)或在 dispatch_request()
:
import inspect
class LoggedView(View):
method_decorators = (log_view,)
#This can be overridden for more complex views
def dispatch_request(self):
# decorate methods
cls = type(self)
members = vars(type(self)).items()
for name, object in members:
if not inspect.isfunction(object):
continue
if name == 'dispatch_request':
continue
# add bound decorated functions to the view
for d in self.method_decorators:
setattr(self, name, d(object).__get__(self, cls))
# dispatch
return render_template(self.template)
这是 Flask-RESTFul 项目用于允许在一行中为视图上的所有方法指定装饰器的路径。
然后从包装器调用参数中提取 self
参数(但将其传递给包装函数):
def log_view(view):
def decorator(self, **kwargs):
try:
#Set up custom logging
self.log = .....
#Execute view
return view(self, **kwargs)
except CustomApplicationError as e:
#Log and display error
self.log.error(e)
return render_template('error.html',error=str(e))
return decorator
我会在视图classes.
外部定义装饰器函数