Как определить обратные вызовы в отдельных файлах? (плотно да sh) - PullRequest
1 голос
/ 30 мая 2020

Фон

Da sh веб-приложения имеют экземпляр приложения da sh, обычно называемый app, и инициируется следующим образом:

app = dash.Dash(__name__)

Затем добавляются обратные вызовы в приложение с помощью callback декоратора:

@app.callback(...)
def my_function(...):
    # do stuff.

В большинстве найденных вами руководств обратные вызовы определяются со всем макетом приложения в app.py. Конечно, это просто способ работы MWE. В реальном приложении разделение кода на модули и пакеты значительно улучшило бы читаемость и удобство обслуживания, но наивное разделение обратных вызовов и макетов приводит к циклическому импорту.

Вопрос

Каким будет правильный способ отделения обратных вызовов и макетов от app.py в одностраничном приложении?

MWE

Вот минимальный (не) рабочий пример с проблемой

Файловая структура

.
├── my_dash_app
│   ├── app.py
│   └── views
│       ├── first_view.py
│       └── __init__.py
└── setup.py

setup.py

import setuptools

setuptools.setup(
    name='dash-minimal-realworld',
    version='1.0.0',
    install_requires=['dash>=1.12.0'],
    packages=setuptools.find_packages(),
)

app.py

import dash

from my_dash_app.views.first_view import make_layout

app = dash.Dash(__name__)
app.layout = make_layout()


if __name__ == '__main__':
    app.run_server(debug=True)

first_view.py

from dash.dependencies import Input, Output

import dash_core_components as dcc
import dash_html_components as html

from my_dash_app.app import app 

def make_layout():
    return html.Div([
        dcc.Input(id='my-id', value='initial value', type='text'),
        html.Div(id='my-div')
    ])

@app.callback(Output(component_id='my-div', component_property='children'),
              [Input(component_id='my-id', component_property='value')])
def update_output_div(input_value):
    return 'You\'ve entered "{}"'.format(input_value)

Запуск python ./my_dash_app/app.py приводит к циклической зависимости:

ImportError: cannot import name 'make_layout' from 'my_dash_app.views.first_view' (c:\tmp\dash_minimal_realworld\my_dash_app\views\first_view.py)

1 Ответ

3 голосов
/ 30 мая 2020

Я не думаю, (но я могу ошибаться) , что есть правильный способ сделать это сам по себе, но вы могли бы сделать это с помощью центрального модуля (maindash.py) вокруг вашего стартапа код app = dash.Dash(__name__), и иметь разные обратные вызовы просто импортировать app из my_dash_app.maindash. Это установит обратные вызовы в их собственных отдельных модулях, но повторно использует этот центральный модуль для экземпляра app.

Проще всего показать его обзор следующим образом: mwe

app.py - главный скрипт, вызываемый для запуска всего. maindash.py отвечает за создание основного экземпляра приложения. В first_view.py определены декораторы для настройки всех обратных вызовов.

Вот результат: enter image description here

.
├── my_dash_app
│   ├── app.py
│   ├── maindash.py
│   └── views
│       ├── first_view.py
│       └── __init__.py
└── setup.py

Поскольку импорт используется повторно в Python нет реального вреда в том, чтобы выполнить from my_dash_app.maindash import app несколько раз из разных других модулей, таких как обработчики событий и основной скрипт. Они будут использовать один и тот же экземпляр импорта - таким образом, повторно используя экземпляр dash.Dash().

Просто убедитесь, что вы импортируете центральный модуль перед настройкой обработчиков, и вам должно быть хорошо go .

Вот фрагменты кода, разделенные для тестирования:

app.py

from my_dash_app.maindash import app
from my_dash_app.views.first_view import make_layout

if __name__ == '__main__':
    app.layout = make_layout()
    app.run_server(debug=True)

mainda sh .py

import dash
app = dash.Dash(__name__)

first_view. ру

from my_dash_app.maindash import app
from dash.dependencies import Input, Output

import dash_core_components as dcc
import dash_html_components as html

def make_layout():
    return html.Div([
        dcc.Input(id='my-id', value='initial value', type='text'),
        html.Div(id='my-div')
    ])

@app.callback(Output(component_id='my-div', component_property='children'),
              [Input(component_id='my-id', component_property='value')])
def update_output_div(input_value):
    return 'You\'ve entered "{}"'.format(input_value)
...