- en
- Language: ru
- Documentation version: 1.1.x
Подключаемые представления¶
Добавлено в версии 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¶
Веб-интерфейсы часто работают очень тесно с 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('%s<%s:%s>' % (url, pk_type, pk), view_func=view_func,
methods=['GET', 'PUT', 'DELETE'])
register_api(UserAPI, 'user_api', '/users/', pk='user_id')