子类化Flask可插入视图以获得可扩展功能的最佳方法

时间:2018-08-03 15:06:17

标签: python inheritance flask views decorator

我正在构建一个Webapp,其中不同的视图将具有不同数量的“包装功能”(例如,身份验证,日志记录/错误处理,数据库访问等),并能够轻松地在视图之间共享此功能。

我认为可插拔视图将是解决此问题的好方法,方法是重复子类化视图以构建功能层,这些功能层包装了视图的主要操作。

但是,我正在努力找出实现此目标的最佳方法。我正在考虑将装饰器链接起来,但是继承似乎无法正常工作。

例如,带有一些自定义日志记录和错误处理的简化视图:

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)

然后,我想重复此子类以添加更多功能,例如数据库访问

  • 是否有更好的方法来实现我的目标?
  • 将装饰器作为查看方法有意义吗?在装饰器中使用“自我”是否有效?

任何建议将不胜感激!

1 个答案:

答案 0 :(得分:0)

decorators是一个列表,一个可变的结构。您不能只是将其追加到子类中。名称decorators没有在子类中定义,如果您将附加到LoggedView.decorators,则会附加到错误的列表!

您必须在子类中创建一个 new 列表对象,以屏蔽基类上的属性;您可以通过连接基类序列来构造一个;我在这里使用元组来使它更清晰:

class LoggedView(View):
    decorators = (log_view,)

class RestrictedView(LoggedView):
    decorators = LoggedView.decorators + (validate_access,)

请注意,装饰器不是方法,它们在应用时未绑定到视图,因此没有self参数。

如果您需要从装饰器访问视图实例,则不要使用View.decorators,它们装饰一个简单的函数,该函数在调用时会创建视图,然后再调用{{1} }调用View.dispatch_request()时将返回此简单函数。另一方面,如果您需要能够访问装饰器,则装饰器在注册路由时或(在另一个方向上)在查找端点的已注册视图时(在另一个方向上)会生成,那么使用View.as_view()是完全正确的。

您可以直接修饰方法(包括View.decorators),也可以在dispatch_request()中实现自己的机制:

dispatch_request()

这是Flask-RESTFul项目用于允许在一行上显示所有方法的装饰器的路径。

然后从包装器调用参数中提取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) 参数(但一定要将其传递给包装的函数):

self

我将在视图类的外部中定义装饰器函数。