Сообщить об ошибке.

Ленивая загрузка представлений в приложении Flask Python.

Для построения URL-адреса функции-представления, фреймворк Flask обычно используется с декораторами @app.route(). Декораторы просты и удобны, к тому же рядом с функцией есть URL-адрес, на запрос которого, она отрисовывает страницу. НО у этого подхода есть обратная сторона: это означает, что весь код, использующий @app.route(), должен быть импортирован заранее, иначе Flask никогда не найдет нужную функцию.

Это может быть проблемой, если приложение большое и должно быстро запускаться, а следовательно - импортировать множество представлений. Поэтому, если приложение переросло во что то большое, или часто получает программные обновления, в ходе которых происходит перезапуск приложения, то стоит подумать о централизованном подходе к сопоставлении URL-адресов.

Централизованный подход к сопоставлении URL-адресов обеспечивает метод экземпляра приложения app.add_url_rule().Идея состоит в том, что бы вместо использования декораторов, создать файл, который создает экземпляр приложения со всеми URL-адресами.

Преобразование в централизованную карту URL-адресов.

Допустим, что текущее приложение выглядит примерно так:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    pass

@app.route('/user/<username>')
def user(username):
    pass

Тогда при централизованном подходе, в приложении будет два файла, один файл с представлениями (views.py) без декораторов:

def index():
    pass

def user(username):
    pass

И второй файл, который создает экземпляр приложения и сопоставляет функции с URL-адресами:

from flask import Flask
from yourapplication import views
app = Flask(__name__)
app.add_url_rule('/', view_func=views.index)
app.add_url_rule('/user/<username>', view_func=views.user)

Отложенная загрузка представлений во Flask.

Пока произошло разделение функций-представлений от соответствующих им URL-адресов, но модуль все еще загружается заранее. Хитрость заключается в том, чтобы фактически загрузить функцию просмотра по мере необходимости. Это может быть выполнено с помощью вспомогательного класса, который ведет себя так же, как функция, но внутренне импортирует реальную функцию при первом использовании:

from werkzeug.utils import import_string, cached_property

class LazyView(object):

    def __init__(self, import_name):
        self.__module__, self.__name__ = import_name.rsplit('.', 1)
        self.import_name = import_name

    @cached_property
    def view(self):
        return import_string(self.import_name)

    def __call__(self, *args, **kwargs):
        return self.view(*args, **kwargs)

Здесь важно то, что __module__ и __name__ были установлены правильно. Эти имена используется Flask внутри, для поиска названий функций-представлений соответствующим правилам URL.

Затем можно переписать файл, который создает экземпляр приложения, следующим образом:

from flask import Flask
from yourapplication.helpers import LazyView
app = Flask(__name__)
app.add_url_rule('/', view_func=LazyView('yourapplication.views.index'))
app.add_url_rule('/user/<username>', view_func=LazyView('yourapplication.views.user'))

Можно еще оптимизировать код выше с точки зрения объема, необходимого для записи правил маршрутизации. Для этого необходимо создать функцию, которая будет вызывать app.add_url_rule().

def url(import_name, url_rules=[], **options):
    # обернем полное название функции-
    # представления в `LazyView`
    view = LazyView(f"yourapplication.{import_name}")
    # пройдемся по всем URL принадлежащим
    # конкретной функции-представления
    for url_rule in url_rules:
        # регистрируем `app.add_url_rule` 
        # при первом обращении к URL
        app.add_url_rule(url_rule, view_func=view, **options)

# единственный маршрут к функции-представлению
# `index()`, расположенной в файле `views.py`
url('views.index', ['/'])

# два маршрута к функции-представлению `user()`, 
# расположенной в файле `views.py`
url_rules = ['/user/', '/user/<username>']
url('views.user', url_rules)

Следует иметь в виду, что обработчики до и после запроса должны быть в файле, который импортирован заранее, чтобы правильно работать с первым запросом.