子类化 Flask 可插拔视图以实现可扩展功能的最佳方式

     2023-02-23     35

关键词:

【中文标题】子类化 Flask 可插拔视图以实现可扩展功能的最佳方式【英文标题】:Best way to subclass Flask pluggable views for extensible functionality 【发布时间】:2019-01-11 12:24:23 【问题描述】:

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

我认为 Pluggable Views 是处理这个问题的好方法,它通过重复子类化视图来构建包装视图主要操作的功能层。

但是,我正在努力找出实现这一点的最佳方法。我正在考虑链接装饰器,但继承似乎效果不佳。

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

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】:

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

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

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

我会在视图类外部定义装饰器函数。

【讨论】:

感谢您的回复。我想也许装饰器作为方法可以访问 self 由于闭包。我不确定该调度请求机制中发生了什么,看起来它以某种方式将类方法绑定到装饰器函数,但实际的装饰发生在哪里?我可以直接用包装层装饰 dispatch_request,或者使用flask global g 来访问我在装饰器函数中需要的东西吗? @FinnAndersen:装饰器应用于View.as_view()返回的新函数;这样做的好处是您也可以通过 Flask API 访问装饰器以获取端点和路由。如果您不需要,那么将装饰器应用于dispatch_request() 就可以了。 as_view() 不是用dispatch_request() 来做路由功能的,所以要应用任何装饰器? @FinnAndersen: 不,as_view() 创建一个新函数,在调用该函数时,会创建视图类的一个实例,并在该新实例上调用dispatch_requestdispatch_request 上的装饰器工作得很好,它们只是没有在 Flask 中注册为端点。有时你需要后者。 好的,感谢您的澄清。将装饰器注册为端点或查找端点的注册视图会有什么好处或用例?

flask学习-22.可插拨视图methodview类(代码片段)

前言可插拨视图基于使用类来代替函数,其灵感来自于Django的通用视图。可插拨视图的主要用途是用可定制的、可插拨的视图来替代部分实现。基本原理假设有一个函数用于从数据库中载入一个对象列表并在模板中渲染:@a... 查看详情

flask_admin笔记三客户化视图

...成的。需要指定一些全局配置参数,首先是实现ModelView的子类并配置全局参数,然后在admin中添加所有的model数据模型:fromflask_admin.contrib.sqlaimportMod 查看详情

如何实现可插拔配置?(代码片段)

大家好,我是3y,一年CRUD经验用十年的markdown程序员👨🏻‍💻常年被誉为职业八股文选手我又又又又被吐槽了,随之而来,我的消息推送平台开源项目Austin又又又又更新啦,迭代自己的项目多是一... 查看详情

servlet3.0的可插拔功能

...听器的声明,从而使得web.xml变为可选配置,那么新增的可插性(pluggability)支持则将Servlet配置的灵活性提升到了新的高度。熟悉Struts2的开发者都知道,Struts2通过插件的形式提供了对包括Spring在内的各种开发框架的支持,开发者甚... 查看详情

异步可插拔协议

】异步可插拔协议【英文标题】:AsynchronousPluggableProtocols【发布时间】:2011-01-0201:16:03【问题描述】:使用this作为参考,我正在尝试创建一个异步可插入协议,该协议仅对我的应用程序临时可用(并且未在系统范围内注册)。... 查看详情

具有 HCS 可插拔共识的 Corda

】具有HCS可插拔共识的Corda【英文标题】:CordawithHCSpluggableconsensus【发布时间】:2020-11-1909:01:06【问题描述】:我正在尝试了解可插拔共识在corda中的适用性。例如,从技术上讲,可以将hederahashgraph的共识服务插入corda。但我不清... 查看详情

javajava扩展机制spi实现

...实现者,通过本地的注册发现获取到具体的实现类,轻松可插拔。  场景:比较典型的一个场景就是JDBC中加载驱动的过程。二、使用demodemo工程结构: 1)首先我们定义一个提供接口的三方包SpiIn 查看详情

可插拔自定义视图 Nibs (Nib-in-a-Nib):内存泄漏 - 为啥?

】可插拔自定义视图Nibs(Nib-in-a-Nib):内存泄漏-为啥?【英文标题】:Pluggablecustom-viewNibs(Nib-in-a-Nib):Memoryleak–why?可插拔自定义视图Nibs(Nib-in-a-Nib):内存泄漏-为什么?【发布时间】:2011-06-1512:17:31【问题描述】:我们当前的best-pra... 查看详情

构建可插拔应用程序:如何包含流行库的分支并防止名称冲突?

】构建可插拔应用程序:如何包含流行库的分支并防止名称冲突?【英文标题】:Buildingpluggableapps:howtoincludeforksofpopularlibrariesandpreventnameconflicts?【发布时间】:2012-08-1009:28:10【问题描述】:我正在构建一个我试图保持可插拔的应... 查看详情

servlet规范之注解与可插拔性(代码片段)

...翻译,尚未校准文章目录Annotationsandpluggability注释和可插拔性@WebServlet@WebFilter@WebInitParam@WebListener@MultipartConfig其他注释/约定Pluggabilityweb.xmlweb.xml的模块化程度web.xml和web-fragment.xml的排序从web.xml、web-fragment.xml和注... 查看详情

如何向新创建的 LISTENER 注册可插拔数据库(PDB)

】如何向新创建的LISTENER注册可插拔数据库(PDB)【英文标题】:HowtoRegisterPluggableDatabase(PDB)withnewcreatedLISTENER【发布时间】:2020-12-0701:32:01【问题描述】:我正在与Oracle19c在centos7合作。安装Oracle后,我使用DBCA创建了带有PluggubleDa... 查看详情

设计模式(代码片段)

...用原则开闭原则对修改关闭,对扩展开发。里氏替换原则子类可以扩展父类的功能,但是不能改变父类原有的功能。比如子类可以覆盖父类的抽象方法(抽象方法在父类中没有实现),但是不能覆盖父类的非抽象方法(非抽象方... 查看详情

kruiserollout:灵活可插拔的渐进式发布框架

前言KruiseRollout是OpenKruise社区开源的渐进式交付框架。KruiseRollout支持配合流量和实例灰度的金丝雀发布、蓝绿发布、A/BTesting发布,以及发布过程能够基于PrometheusMetrics指标自动化分批与暂停,并提供旁路的无感对接、兼... 查看详情

kruiserollout:灵活可插拔的渐进式发布框架

前言KruiseRollout是OpenKruise社区开源的渐进式交付框架。KruiseRollout支持配合流量和实例灰度的金丝雀发布、蓝绿发布、A/BTesting发布,以及发布过程能够基于PrometheusMetrics指标自动化分批与暂停,并提供旁路的无感对接、兼... 查看详情

服务消费(ribbonfeign)

...并用注解来配置它即可完成对Web服务接口的绑定。它具备可插拔的注解支持,包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器.SpringCloudFeign还扩展了对SpringMVC注解的支持,同时还整合了Ribbon来提供均衡负载的HTTP客户... 查看详情

pytorch实现对卷积的可插拔reparameterization(代码片段)

需要实现对卷积层的重参数化reparameterization但是代码里卷积前weight并没有hook,很难在原本的卷积类上用pureoo的方式实现目前的解决方案是继承原本的卷积,挂载一个weightmodule替代原本的weightparameter。需要hack一下getattr大致代码:... 查看详情

flask扩展缓存

...使用Flask-Cache扩展实现缓存功能之前,我们先来自己写个视图缓存装饰器,方便我们来理解视图缓存的实现。首先,我们要有一个缓存,Werkzeug框架中的提供了一个简单的缓存对象SimpleCache,它是将缓存项存放在Python解释器的内存... 查看详情

springcloud构建微服务架构—服务消费(feign)

...并用注解来配置它既可完成对Web服务接口的绑定。它具备可插拔的注解支持,包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器。SpringCloudFeign还扩展了对SpringMVC注解的支持 查看详情