Декораторы представлений
В 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', '') }}"/>
Декоратор кэширования
Представьте, что у вас есть функция представления, которая выполняет дорогостоящее вычисление, и поэтому вы хотели бы кэшировать полученные результаты на определенный период времени. Декоратор отлично подойдет для этого. Мы предполагаем, что вы настроите кэш, как описано в Кэшировании.
Вот пример функции кэширования. Она генерирует ключ кэша из определенного префикса (на самом деле строка форматирования) и текущего пути запроса. Обратите внимание, что мы используем функцию, которая сначала создает декоратор, а затем декорирует функцию. Звучит ужасно? К сожалению, это немного сложнее, но код должен быть достаточно понятным для чтения.
Декорированная функция будет работать следующим образом
- получить уникальный ключ кэша для текущего запроса на основе текущего пути.
- получить значение для этого ключа из кэша. Если кэш вернул что-то, мы вернем это значение.
- в противном случае вызывается исходная функция, а возвращаемое значение сохраняется в кэше на указанный период времени ожидания (по умолчанию 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'. В противном случае используется предоставленное имя шаблона. Когда декорированная функция возвращает значение, возвращаемый словарь передается функции рендеринга шаблона. Если 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"
© 2010 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/3.0.x/patterns/viewdecorators/