__init__ моего Flask-Admin ModelView не имеет контекста приложения — когда он обычно его получает?

У меня есть рабочий пример приложения Flask-Admin на GitHub, которое запрашивает представление ( сам основан на MySQL information_schema.TABLES) для динамического обновления свойств ModelView column_labels и column_descriptions. Также есть небольшая модификация шаблона для добавления всплывающих подсказок в строку заголовка, содержащую комментарии столбца из базы данных.

Я приложил много усилий, чтобы понять это, потому что казалось глупым повторно вводить описания столбцов в моем коде Python, когда они уже были введены в базу данных с помощью ключевого слова COMMENT SQL во время создания таблицы.

В простом тестовом приложении все работает как положено; Я получаю параметры model и session, переданные методу __init__ ModelView, и использую session для запроса другой модели меток/комментариев столбцов, обновляю self.column_labels и self.column_descriptions в представлении, затем вызываю super().

Анимированный GIF-файл с подсказками столбца

Однако в более сложном приложении я получаю ужасный RuntimeError: No application found. Either work inside a view function or push an application context, когда пытаюсь запросить другую модель в методе __init__ ModelView. Единственная заметная разница, которую я бы отметил между демонстрационным приложением и моим «настоящим» приложением, заключается в том, что я импортирую объект SQLAlchemy, созданный в другом файле .py, а затем вызываю его init_app() в моем app.py, чтобы подключить его к экземпляру Flask.

Изменить: что именно является проблемой; SQLAlchemy экземпляров, созданных с использованием обычного способа, продемонстрированных в Flask -SQLAlchemy Quickstart получить правильный экземпляр приложения Flask в своих __init__()s; когда я использую db.init_app(app), вместо этого я получаю ошибку No application found.

У меня такой вопрос: в какой момент Flask-Admin ModelView начинает существовать внутри контекста приложения Flask? Почему условия для import db from somewhere.py; db.init_app(app) и db = SQLAlchemy(app) различаются? Есть ли способ, которым я могу отследить процесс запуска приложения Flask и подключиться к этому моменту, чтобы увидеть, что здесь происходит?

Здесь задействованы две основные части (полный исходный код для каждый включен GitHub, как указано выше):

Модель, предоставляющая столбец «метаданные», включая комментарии.

# models.py
# [SQLALchemy imports and declaration of 'Base']
class ColumnProperty(Base):
    # this view is based on MySQL's 'information_schema.TABLES'
    __tablename__ = 'v_column_properties'

    parent_table = Column(String(64), primary_key=True)
    name = Column(String(64), primary_key=True)
    # [some fields elided]
    comment = Column(String(1024)) # type: str

а также

ModelView, который запрашивает указанную выше модель при создании экземпляра.

# views.py
from flask_admin.contrib.sqla import ModelView

class TestView(ModelView): 
    def __init__(self, model, session, **kwargs):
        from models import ColumnProperty as cp

        descriptions = {}
        q = session.query(cp).filter(cp.parent_table==model.__tablename__)

        for row in q.all():
            descriptions[row.name] = row.comment

        self.column_descriptions = descriptions 
        super(TestView, self).__init__(model, session, **kwargs)

person TheDudeAbides    schedule 22.05.2018    source источник
comment
Как показано в этой ветке, что различие между простым демо-приложением и моим реальным приложением Flask-Admin является проблемой. Что еще больше меня смущает, почему вообще кто-то вообще может использовать init_app().   -  person TheDudeAbides    schedule 22.05.2018


Ответы (1)


Я бы переопределил метод render для ModelView и установил там ваш column_descriptions. Очевидно, вы можете настроить какой-то механизм кэширования, чтобы уменьшить количество запросов к базе данных.

Что-то вроде (полностью не проверено) - обратите внимание на использование self.session в запросах:

# views.py
from flask_admin.contrib.sqla import ModelView

class TestView(ModelView):

    def render(self, template, **kwargs):
        from models import ColumnProperty as cp

        descriptions = {}
        q = self.session.query(cp).filter(cp.parent_table==model.__tablename__)

        for row in q.all():
            descriptions[row.name] = row.comment

        self.column_descriptions = descriptions

        super(TestView, self).render(template, **kwargs)
person pjcunningham    schedule 22.05.2018
comment
Это было одной из вещей, которые я рассматривал, но предполагается, что запрос начинается с представления индекса, а не пользователь переходит прямо, скажем, к форме создания, где эти описания столбцов также должны быть уже установлены. - person TheDudeAbides; 23.05.2018
comment
Инициализация column_labels и column_descriptions в __init__ действительно работает так, как вы ожидаете, если вы не используете db.init_app. Я думаю, некоторый намек содержится в документации по API Flask-SQLAlchemy, где упоминается, что db = SQLAlchemy(app) и db.init_app(app) отличаются тем, что в первом случае такие методы, как create_all() и drop_all(), будут работать постоянно, а во втором случае должен существовать flask.Flask.app_context(). Я надеюсь лучше понять почему, прежде чем либо отвечу на свой вопрос, либо закрою его как неконструктивный. - person TheDudeAbides; 23.05.2018
comment
@TheDudeAbides — метод рендеринга вызывается в представлении index/new/edit. Возможно, это место для вашей инициализации. - person pjcunningham; 23.05.2018