Посмотреть декораторов

В 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 после запроса GET на страницу входа. Вам нужно будет передать его при отправке запроса POST из формы входа. Вы можете сделать это с помощью скрытого тега input, а затем получить его из 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/{}'):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            cache_key = key.format(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'. В противном случае используется имя предоставленного шаблона. Когда функция decorated возвращается, возвращаемый словарь передается в функцию рендеринга шаблона. Если возвращается 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 = f"{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"
Вернуться на верх