Spec-Zone.ru › Flask 2.0

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

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

Новая версия 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(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/

Spec-Zone.ru

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