Spec-Zone.ru › Flask 1.1

Декораторы представлений

Python имеет очень интересную функцию, называемую декораторами функций. Это позволяет сделать некоторые очень интересные вещи для веб-приложений. Поскольку каждое представление в Flask является функцией, декораторы могут использоваться для вставки дополнительной функциональности в одну или несколько функций. Декоратор route() — тот, который вы, вероятно, уже использовали. Но есть случаи использования для реализации собственного декоратора. Например, представьте, что у вас есть представление, которое должно использоваться только авторизованными пользователями. Если пользователь переходит на сайт и не авторизован, он должен быть перенаправлен на страницу входа в систему. Это хороший пример случая использования, в котором декоратор является отличным решением.

Декоратор, требующий авторизации

Итак, давайте реализуем такой декоратор. Декоратор — это функция, которая оборачивает и заменяет другую функцию. Поскольку исходная функция заменяется, вам нужно запомнить, чтобы скопировать информацию об исходной функции в новую функцию. Используйте functools.wraps(), чтобы справиться с этим за вас.

Этот пример предполагает, что страница входа в систему называется 'login', а текущий пользователь хранится в g.user и является None, если никто не авторизован.

from functools import wraps
from flask import g, request, redirect, url_for

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if g.user is None:
            return redirect(url_for('login', next=request.url))
        return f(*args, **kwargs)
    return decorated_function

Чтобы использовать декоратор, примените его как самый внутренний декоратор к функции представления. При применении дополнительных декораторов всегда помните, что декоратор route() является самым внешним.

@app.route('/secret_page')
@login_required
def secret_page():
    pass

Примечание

Значение next будет существовать в request.args после запроса на страницу входа в систему. Вам нужно будет передать его при отправке запроса POST с формы входа в систему. Вы можете сделать это с помощью тега скрытого ввода, а затем извлечь его из request.form при входе в систему пользователя.

<input type="hidden" value="{{ request.args.get('next', '') }}"/>

Декоратор кэширования

Представьте, что у вас есть функция представления, которая выполняет дорогостоящее вычисление, и поэтому вы хотите кэшировать полученные результаты на определенный период времени. Декоратор будет полезен для этого. Мы предполагаем, что вы настроили кэш, как описано в Кэшировании.

Вот пример функции кэширования. Она генерирует ключ кэша из определенного префикса (фактически строка форматирования) и текущего пути запроса. Обратите внимание, что мы используем функцию, которая сначала создает декоратор, а затем декорирует функцию. Звучит ужасно? К сожалению, это немного сложнее, но код должен быть по-прежнему легко читаемым.

Декорированная функция будет работать следующим образом:

  1. получить уникальный ключ кэша для текущего запроса на основе текущего пути.
  2. получить значение для этого ключа из кэша. Если кэш вернул что-то, мы вернём это значение.
  3. в противном случае вызывается исходная функция, а возвращаемое значение сохраняется в кэше на указанный срок действия (по умолчанию 5 минут).

Вот код:

from functools import wraps
from flask import request

def cached(timeout=5 * 60, key='view/%s'):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            cache_key = key % request.path
            rv = cache.get(cache_key)
            if rv is not None:
                return rv
            rv = f(*args, **kwargs)
            cache.set(cache_key, rv, timeout=timeout)
            return rv
        return decorated_function
    return decorator

Обратите внимание, что это предполагает, что доступен экземпляр объекта cache, см. Кэширование для получения дополнительной информации.

Декоратор шаблонизации

Распространённый шаблон, придуманный парнями из TurboGears некоторое время назад, — это декоратор шаблонизации. Идея этого декоратора заключается в том, что вы возвращаете словарь с значениями, переданными в шаблон из функции представления, и шаблон автоматически отображается. С этим следующие три примера делают ровно то же самое:

@app.route('/')
def index():
    return render_template('index.html', value=42)

@app.route('/')
@templated('index.html')
def index():
    return dict(value=42)

@app.route('/')
@templated()
def index():
    return dict(value=42)

Как видите, если имя шаблона не указано, используется конечная точка карты URL с точками, преобразованными в слэши + '.html'. В противном случае используется указанное имя шаблона. Когда декорированная функция возвращает значение, возвращаемый словарь передаётся функции отображения шаблона. Если None возвращается, предполагается пустой словарь, если возвращается что-то другое, чем словарь, мы возвращаем это из функции без изменений. Таким образом, вы всё ещё можете использовать функцию перенаправления или возвращать простые строки.

Вот код этого декоратора:

from functools import wraps
from flask import request, render_template

def templated(template=None):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            template_name = template
            if template_name is None:
                template_name = request.endpoint \
                    .replace('.', '/') + '.html'
            ctx = f(*args, **kwargs)
            if ctx is None:
                ctx = {}
            elif not isinstance(ctx, dict):
                return ctx
            return render_template(template_name, **ctx)
        return decorated_function
    return decorator

Декоратор конечной точки

Если вы хотите использовать систему маршрутизации Werkzeug для большей гибкости, вам нужно сопоставить конечную точку, как определено в Rule с функцией представления. Это возможно с помощью этого декоратора. Например:

from flask import Flask
from werkzeug.routing import Rule

app = Flask(__name__)
app.url_map.add(Rule('/', endpoint='index'))

@app.endpoint('index')
def my_index():
    return "Hello world"

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

Spec-Zone.ru

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