Так как каждое представление во Flask - это простая функция Python, то при проектировании и создании приложений можно использовать собственные декораторы, что позволяет уменьшить количество кода функций-представлений и делать полезные вещи.
render_template()
.endpoint
.Представим, что есть функция-представление, которая должна отображать страницу сайта только пользователями, которые вошли в систему. Если пользователь переходит на эту страницу и не вошел в систему, то его необходимо перенаправить на страницу входа. Это хороший пример варианта использования, в котором декоратор - отличное решение.
Декоратор - это функция, которая обертывает и заменяет другую функцию. Так как исходная функция заменяется, то необходимо скопировать информацию исходной функции в новую функцию при помощи 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: # дополнительно в URL передаем параметр `next` # что бы после авторизации перенаправить его # на страницу, к которой он обращался return redirect(url_for('login', next=request.url)) return f(*args, **kwargs) return decorated_function
Чтобы применить этот декоратор к функции-представлению, его необходимо поместить как самый внутренний декоратор. При применении дополнительных декораторов всегда помните, что декоратор @app.route()
является самым внешним.
@app.route('/secret_page') @login_required def secret_page(): pass
Примечание. После GET
запроса для страницы входа, в request.args
будет существовать значение next
. Это значение нужно передать при отправке запроса POST из формы входа. Это можно сделать с помощью скрытого тега <Input>
, а затем получить его из request.form
при входе пользователя в систему.
<input type="hidden" value="{{ request.args.get('next', '') }}"/>
Представим, что есть функция-представление, которая выполняет дорогостоящие вычисления, а сгенерированные результаты необходимо кэшировать на определенное время.
Создаваемый кэширующий-декоратор будет генерировать ключ кэша из определенного префикса, в данном случае из текущего URL-адреса.
Декорированная функция-представление будет работать следующим образом:
Для создания такого декоратора понадобится шаблон декоратора с параметрами
Пример декоратора, который кэширует функцию-представление:
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
всегда доступен.
Так же рекомендуем рассмотреть использование в приложении расширения Flask-Caching
, которое умеет хранить кэш в памяти, на жестком диске или в различных базах данных и легко настраивается.
render_template()
.Идея такого декоратора заключается в том, что бы из функции-представления возвращать словарь со значениями контекста, передаваемый в функцию render_template()
. Например при использовании декоратора @templated
(его код рассматривается ниже) следующие три примера делают одно и то же:
@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)
Если в @templated
имя шаблона не передается, то декоратор будет создавать его из конечной точки endpoint
, которая передается в функцию add_url_rule()
. Если функция-представление возвращает словарь, то он передается в функцию отрисовки шаблона render_template()
. Если функция-представление возвращает None
, то в render_template()
передается пустой словарь, если возвращается что-то еще, кроме словаря и None
, то декоратор возвращает это значение без изменений. Таким образом, сохраняется возможность использовать функцию redirect()
или возвращать простые строки.
Пример декоратора для функции render_template()
:
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: # получаем имя шаблона из `request.endpoint` template_name = f"{request.endpoint.replace('.', '/')}.html" ctx = f(*args, **kwargs) if ctx is None: # если функция-представление ничего # не возвращает, то используем пустой словарь ctx = {} elif not isinstance(ctx, dict): # если функция-представление возвратила # НЕ None и НЕ словарь (например ответ # или пустую строку) то просто отдаем это значение return ctx return render_template(template_name, **ctx) return decorated_function return decorator
endpoint
.Если предпочтительнее в приложении Flask, для большей гибкости, использовать систему маршрутизации модуля werkzeug
, то необходимо сопоставить конечную точку endpoint
, которая передается в Rule()
, с функцией-представлением. Это возможно с помощью следующего встроенного декоратора @app.endpoint()
.
Пример:
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"