Spec-Zone.ru › Flask 1.1

Подключаемые представления

Журнал изменений

Новое в версии 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МетодОписание

/users/

GET

Выводит список всех пользователей

/users/

POST

Создает нового пользователя

/users/<id>

GET

Отображает одного пользователя

/users/<id>

PUT

Обновляет одного пользователя

/users/<id>

DELETE

Удаляет одного пользователя

Итак, как вы это сделаете с 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')
{

© 2007–2020 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/0.12.x/views/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API