.. _views: Сменные представления (Pluggable Views) ======================================= .. versionadded:: 0.7 В Flask 0.7 появились сменные представления, идея которых взята из обычных представлений (generic views) Django, основанных не на функциях, а на классах. Их основное назначение - чтобы вы путём замены части реализации могли бы получить настраиваемые сменные представления. Основной принцип ---------------- К примеру, у вас есть функция, загружающая список объектов из базы данных и отображающая их в шаблон:: @app.route('/users/') def show_users(page): users = User.query.all() return render_template('users.html', users=users) Это и гибко, и просто, однако если вы хотите обеспечить это представление в общем виде, которое может быть адаптировано также и к другим моделям и шаблонам, вам может потребоваться дополнительная гибкость. Этот тот случай, когда хорошо подходят сменные представления на базе классов. Первым делом для преобразования к представлению на основе классов, выполним следующее:: from flask.views import View class ShowUsers(View): def dispatch_request(self): users = User.query.all() return render_template('users.html', objects=users) app.add_url_rule('/users/', view_func=ShowUsers.as_view('show_users')) Как видите, вам необходимо создать подкласс класса :class:`flask.views.View` и реализовать :meth:`~flask.views.View.dispatch_request`. Далее нам надо преобразовать этот класс в функцию актуального представления с использованием метода класса :meth:`~flask.views.View.as_view`. Строка, которую вы передаёте в эту функцию - это имя конечной точки, к которой придёт представление. Однако само по себе это бесполезно, так что давайте немного переработаем код:: from flask.views import View class ListView(View): def get_template_name(self): raise NotImplementedError() def render_template(self, context): return render_template(self.get_template_name(), **context) def dispatch_request(self): context = {'objects': self.get_objects()} return self.render_template(context) class UserView(ListView): def get_template_name(self): return 'users.html' def get_objects(self): return User.query.all() Конечно, это не так полезно для такого вот небольшого примера, однако этого достаточно, чтобы раскрыть простейшие принципы. Когда у вас есть основанное на классе представление, встаёт вопрос, на что указывает `self`. Это работает следующим образом. Всякий раз при диспетчеризации запроса, создаётся новый экземпляр класс и вызывается метод :meth:`~flask.views.View.dispatch_request` с параметрами из URL-правила. Сам класс создаётся с параметрами, переданными функции :meth:`~flask.views.View.as_view`. Например, вы можете создать класс, похожий на вот этот:: class RenderTemplateView(View): def __init__(self, template_name): self.template_name = template_name def dispatch_request(self): return render_template(self.template_name) Теперь вы можете зарегестрировать его подобным образом:: app.add_url_rule('/about', view_func=RenderTemplateView.as_view( 'about_page', template_name='about.html')) Подсказки метода ---------------- Сменные представления присоединяются к приложению подобно обычной функции с помощью использования :func:`~flask.Flask.route` или лучше :meth:`~flask.Flask.add_url_rule`. Однако это будет также означать, что при их присоединении вам придётся предоставить имена HTTP-методов, поддерживаемых представлением. Чтобы переместить эту информацию в класс, вам надо предоставить атрибут :attr:`~flask.views.View.methods`, который содержит следующую информацию:: class MyView(View): methods = ['GET', 'POST'] def dispatch_request(self): if request.method == 'POST': ... ... app.add_url_rule('/myview', view_func=MyView.as_view('myview')) Диспетчеризация на базе метода ------------------------------ Для различных RESTful API, особенно полезно выполнять различные функции для каждого из методов HTTP. Вы можете легко это осуществить с помощью :class:`flask.views.MethodView`. Каждый HTTP-метод сопоставляется с функцией, носящей такое же имя (но в нижнем регистре):: from flask.views import MethodView class UserAPI(MethodView): def get(self): users = User.query.all() ... def post(self): user = User.from_form_data(request.form) ... app.add_url_rule('/users/', view_func=UserAPI.as_view('users')) При этом вам нет нужды предоставлять атрибут :attr:`~flask.views.View.methods`. Он будет автоматически установлен исходя из определённых в классе методов. Декорирующие представления -------------------------- Так как сам по себе класс предcтавления не является функцией представления, которая добавляется в подсистему маршрутизации, не имеет особого смысла декорировать сам класс. Вместо этого вы должны вручную декорировать возвращаемое :meth:`~flask.views.View.as_view` значение:: def user_required(f): """Checks whether user is logged in or raises error 401.""" def decorator(*args, **kwargs): if not g.user: abort(401) return f(*args, **kwargs) return decorator view = user_required(UserAPI.as_view('users')) app.add_url_rule('/users/', view_func=view) Начиная с Flask 0.8 есть альтернативный способ, при котором вы можете указать список декораторов, которые необходимо применить при декорировании класса:: class UserAPI(MethodView): decorators = [user_required] В связи с неявным self с точки зрения вызывающего объекта, вы не можете использовать для индивидуальных методов обычные декораторы представления, однако, имейте это ввиду. Представления метода для различных API -------------------------------------- Веб-API часто работают очень близко с методами HTTP, так что есть большой смысл реализовывать такие API на базе :class:`~flask.views.MethodView`. Тем не менее вы заметите, что API требуют различные URL-правила, которые чаще всего передавались тому же методу представления. Рассмотрим для примера, что вы делаете доступным через web объект пользователя (user): =============== =============== ====================================== URL Method Description --------------- --------------- -------------------------------------- ``/users/`` ``GET`` Вывести список всех пользователей ``/users/`` ``POST`` Создать нового пользователя ``/users/`` ``GET`` Показать одного пользователя ``/users/`` ``PUT`` Обновить одного пользователя ``/users/`` ``DELETE`` Удалить одного пользователя =============== =============== ====================================== Итак, как вам это сделать с помощью :class:`~flask.views.MethodView`? Хитрость в том, что можно воспользоваться предоставлением нескольких правил для одного и того же представления. Предположим, что на данный момент представление будет выглядеть следующим образом:: class UserAPI(MethodView): def get(self, user_id): if user_id is None: # возвратить список пользователей pass else: # показать одного пользователя pass def post(self): # создать нового пользователя pass def delete(self, user_id): # удалить одного пользователя pass def put(self, user_id): # обновить одного пользователя pass Итак, как же нам подцепить это к подсистеме маршрутизации? Путём добавления двух правил и оговаривая для каждого из них методы:: user_view = UserAPI.as_view('user_api') app.add_url_rule('/users/', defaults={'user_id': None}, view_func=user_view, methods=['GET',]) app.add_url_rule('/users/', view_func=user_view, methods=['POST',]) app.add_url_rule('/users/', view_func=user_view, methods=['GET', 'PUT', 'DELETE']) Если у вас много похожих API, вы можете реорганизовать такой вот код регистрации:: def register_api(view, endpoint, url, pk='id', pk_type='int'): view_func = view.as_view(endpoint) app.add_url_rule(url, defaults={pk: None}, view_func=view_func, methods=['GET',]) app.add_url_rule(url, view_func=view_func, methods=['POST',]) app.add_url_rule('%s<%s:%s>' % (url, pk_type, pk), view_func=view_func, methods=['GET', 'PUT', 'DELETE']) register_api(UserAPI, 'user_api', '/users/', pk='user_id') `Оригинал этой страницы `_