在Flask中为类添加REST行为,蓝图的用途?
我正在处理一个Python应用程序,这个程序由多个轻量级的分布式组件组成,它们通过RabbitMQ和Kombu进行通信。
其中一个组件监听两个队列,并且可以在每个队列上接收多种类型的消息。子类可以通过注册自定义处理程序来覆盖每种消息类型的处理方式。所有这些都运行得很好。
现在我有了一个新的要求:每个组件必须有一个基本的REST/HTML接口。也就是说,你可以用浏览器访问这个正在运行的组件,实时查看它当前在做什么(比如正在处理哪些消息、CPU使用情况、状态信息、日志等)。
这个接口需要轻量,所以经过一些研究,我决定使用Flask(但我也愿意听取其他建议)。用伪代码来说,这意味着我需要:
class Component:
Queue A
Queue B
...
def setup(..):
# connect to the broker & other initialization
def start(..):
# start the event loop and wait for work
def handle_msg_on_A(self,msg):
# dispatch a msg to a handler depending on the msg type
def handle_msg_on_B(self,msg):
...
...
并添加一些视图方法:
@app.route('/')
def web_ui(self):
# render to a template
@app.route('/state')
def get_state(self):
# REST method to return some internal state info as JSON
...
不过,把一个网页界面直接加到这样的类上,会破坏SOLID原则,并且会带来继承方面的问题(子类可能想显示更多或更少的信息)。装饰器是不会被继承的,所以每个视图方法都需要明确地被重写和重新装饰。也许使用混合类和反射可以解决这个问题,但感觉有点不太靠谱。
相反,使用组合的方式可能会更好:把网页相关的内容放在一个单独的类中,这个类将URL路由委托给嵌套组件上固定的、多态的方法。这样,组件就不会知道Flask的存在,但会牺牲一些灵活性(可用的方法集合是固定的)。
我现在发现了Flask蓝图和应用调度,看起来它们可以提供一个更好、更可扩展的解决方案。不过,我还没有完全理解它们。
我觉得我在这里缺少一个设计模式,希望有更多Flask经验的人能给我一些建议。
1 个回答
在Flask 0.7版本中,还有一个新功能可能会引起你的兴趣——可插拔视图。这个功能是基于类的,而不是基于函数的端点。也就是说,你可以使用dispatch_request
方法来管理状态的变化(只有在需要的时候才重写这个方法)。
这样做的好处是,相比于使用应用程序调度,你可以在整个应用中使用url_for
,而不需要在跨应用的地方硬编码网址。你需要考虑一下,这是否会成为你应用中的一个问题。
用伪代码表示:
# File: Components.py
from flask.views import View
class Component(View):
# Define your Component-specific application logic here
dispatch_request(self, *url_args, **url_kwargs):
# Define route-specific logic that all Components should have here.
# Call Component-specific methods as necessary
class Tool_1(Component):
pass
class Tool_2(Component):
# Override methods here
# File: app.py
from flask import Flask
from yourapplication import Tool_1, Tool_2
app = Flask()
# Assuming you want to pass all additional parameters as one argument
app.add_url_rule("/tool_1/<path:options>", "tool1", view_func=Tool_1.as_view())
# Assuming you want to pass additional parameters separately
tool_2_view = Tool_2.as_view()
app.add_url_rule("/tool_2/", "tool2", view_func=tool_2_view )
app.add_url_rule("/tool_2/<option>", "tool2", view_func=tool_2_view)
app.add_url_rule("/tool_2/<option>/<filter>", "tool2", view_func=tool_2_view)
如果你有一系列逻辑上相互关联的组件,你可以添加蓝图,这样就不需要记得在每个add_url_rule
调用前加上/prefix
。但是如果你的组件大多数是独立的,我建议使用这种模式*。
*.另一方面,如果它们需要相互隔离,我会使用文档中推荐的应用程序调度模式。