Подключаемые представления
Журнал изменений
Новая версия 0.7.
Flask 0.7 представляет подключаемые представления, вдохновлённые обобщёнными представлениями 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'))
Как видите, необходимо создать подкласс класса flask.views.View и реализовать метод dispatch_request(). Затем необходимо преобразовать этот класс в фактическую функцию представления с помощью метода класса 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 указывает. При обработке запроса создаётся новый экземпляр класса, и вызывается метод dispatch_request() с параметрами из правила URL. Сам класс инициализируется параметрами, переданными функции 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'))
Подсказки методов
Подключаемые представления прикрепляются к приложению, как обычная функция, используя route(), или лучше add_url_rule(). Однако это также означает, что при прикреплении необходимо указать имена HTTP-методов, которые поддерживает представление. Для переноса этой информации в класс можно указать атрибут 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-метода. С помощью класса 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'))
Таким образом, вам также не нужно указывать атрибут methods. Он автоматически устанавливается на основе методов, определённых в классе.
Декорирование представлений
Поскольку сам класс представления не является функцией представления, добавляемой в систему маршрутизации, применять декораторы к классу не имеет смысла. Вместо этого необходимо вручную декорировать результат работы 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 на основе класса MethodView имеет смысл. При этом вы заметите, что API, как правило, потребует разных правил URL, указывающих на то же представление методов.
URL | Метод | Описание |
|
| Отображает список всех пользователей |
|
| Создаёт нового пользователя |
|
| Отображает одного пользователя |
|
| Обновляет одного пользователя |
|
| Удаляет одного пользователя |
Как вы это сделаете с помощью MethodView? Секрет в том, что вы можете предоставить несколько правил для одного представления.
Предположим, что представление будет выглядеть так:
class UserAPI(MethodView):
def get(self, user_id):
if user_id is None:
# return a list of users
pass
else:
# expose a single user
pass
def post(self):
# create a new user
pass
def delete(self, user_id):
# delete a single user
pass
def put(self, user_id):
# update a single user
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/<int:user_id>', 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(f'{url}<{pk_type}:{pk}>', view_func=view_func,
methods=['GET', 'PUT', 'DELETE'])
register_api(UserAPI, 'user_api', '/users/', pk='user_id')
© 2007–2021 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/2.0.x/views/