Может ли Flask обеспечить маршрутизацию в стиле CherryPy?
Стиль маршрутизации CherryPy по умолчанию основан на экземплярах классов с методами, декорированными @cherrypy.expose
,
В приведенном ниже примере эти URL-адреса предоставляются путем простой настройки обычных классов.
/
/hello
/hello/again
/bye
/bye/again
Интересно, есть ли способ добиться этого с помощью @route Flask или другого декоратора.
import cherrypy
class Root(object):
@cherrypy.expose
def index(self):
return 'my app'
class Greeting(object):
def __init__(self, name, greeting):
self.name = name
self.greeting = greeting
@cherrypy.expose
def index(self):
return '%s %s!' %(self.greeting, self.name)
@cherrypy.expose
def again(self):
return '%s again, %s!' %(self.greeting, self.name)
if __name__ == '__main__':
root = Root()
root.hello = Greeting('Foo', 'Hello')
root.bye = Greeting('Bar', 'Bye')
cherrypy.quickstart(root)
1 ответ
Чтобы сделать что-то подобное, вам придется использовать немного магии питона, однако это абсолютно возможно сделать с колбой. Самый простой способ скопировать ваш пример - использовать фляжку подкласса. Это один из способов сделать это:
import inspect
from flask import Flask
def expose(f):
"""Decorator that flags a method to be exposed"""
f._exposed_method = True
return f
class FlaskOfCherryPy(Flask):
"""Custom flask that allows cherrypy's expose decorator"""
def quickstart(self, root_handler, *args, **kwargs):
self._process_root_handler(root_handler)
self.run(*args, **kwargs)
def _process_root_handler(self, root_handler):
# Prime the recursive processing
root_url = []
self._process_a_handler(root_handler, root_url)
def _process_a_handler(self, current_handler, url_stack):
# This gives a list of all the members of current_handler
members = inspect.getmembers(current_handler)
for name, value in members:
# You probably want to skip things that start with a _ or __
if name.startswith('_'):
continue
# Check if the method is decorated
is_exposed_method = getattr(value, '_exposed_method', False)
# If it's a callable with the _exposed_method attribute set
# Then it's an exposed method
if is_exposed_method and callable(value):
self._add_exposed_url(url_stack, name, value)
else:
new_stack = url_stack[:]
new_stack.append(name)
self._process_a_handler(value, new_stack)
def _add_exposed_url(self, url_stack, name, view_func):
copied_stack = url_stack[:]
if name != 'index':
copied_stack.append(name)
url = "/%s" % "/".join(copied_stack)
if name == 'index':
copied_stack.append(name)
view_name = "_".join(copied_stack)
self.add_url_rule(url, view_name, view_func)
class Root(object):
@expose
def index(self):
return 'my app'
class Greeting(object):
def __init__(self, name, greeting):
self.name = name
self.greeting = greeting
@expose
def index(self):
return '%s %s!' %(self.greeting, self.name)
@expose
def again(self):
return '%s again, %s!' %(self.greeting, self.name)
if __name__ == '__main__':
root = Root()
root.hello = Greeting('Foo', 'Hello')
root.bye = Greeting('Bar', 'Bye')
app = FlaskOfCherryPy(__name__)
app.quickstart(root)
По сути, трюк захватывает все методы, которые помечены _exposed_method
атрибут и передавая их Flask.add_url_rule
( см. документы здесь). Прелесть колбы в том, что это такая легкая система, что не очень страшно ее расширять. Я настоятельно рекомендую окунуться в себя и дать ему шанс, но я получил удовольствие, решая ваш вопрос, поэтому здесь у меня есть сценарий.
Этот конкретный код, который я написал, не идеален и не был тщательно протестирован, но он определенно работает для вашего конкретного случая использования. Кроме того, это не обязательно, как вы хотели бы запустить приложение в производстве. Для этого вам нужно создать какую-то фабрику приложений. Опять же, я настоятельно рекомендую заглянуть внутрь колбы, чтобы заставить ее делать то, что вы хотите. Вы также можете посмотреть на классовые виды или чертежи, которые предлагает колба. Они способны на некоторые вещи так же, как вы их здесь. Конечно, их использование сильно отличается, и установка атрибутов экземпляров, как я знаю, невозможна при использовании ванильных чертежей. Опять же, вы всегда можете расширить:-)