-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add separate init_app function for providing the flask app #739
Conversation
Not entirely sure where to write tests for this. If there's already simple test that tests Dash + external Flask app, we could copy that and edit it to test my |
that's a good point, we should be more explicit in the guide. *take note that the whole testing part will be refactored soon, but you are safe to follow the existing pattern. * |
dash/dash.py
Outdated
@@ -108,8 +108,13 @@ def __init__( | |||
components_cache_max_age=None, | |||
show_undo_redo=False, | |||
plugins=None, | |||
defer_app=False, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like our kwargs are getting a bit out of hand - can we accomplish this without adding a new one? Like, perhaps switch the server
kwarg default to 'new'
, and that creates a new Flask
app rather than looking for falsy like we do now, but you can pass in None
(or really anything that's neither 'new'
nor an instance of Flask
) and we'll treat that like your defer_app=True
.
@chriddyp does this make sense to you or is my aversion to new kwargs misplaced?
Either way it's well past time that we make a docstring for this class describing all the kwargs! Not your problem @TMiguelT, I'll take care of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I avoided changing the meaning of any kwargs is because that would break backwards compatibility. However, we could still have a server='defer'
mode that would reduce the number of kwargs and not break anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I appreciate your effort to maintain compatibility. This would be a pretty minor breakage though - only if someone explicitly provides a falsy server
and expects it to generate the automatic one. I won't claim nobody has a use case for that, but I certainly can't think of a common one. And if we're going to be making breaking changes, now is the time - the next release will be dash 1.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I conceptually agree that server
seems like a decent place for this, given that they have very similar intentions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, so how about:
server=True
: Create new serverserver=False
: Defer creating serverserver=Flask()
: Use existing flask server
Another issue I've encountered is that, if the you initialise the app later, you probably don't have a database connection yet, so you can't populate the layout using database queries. For this reason, I would like to have a new kind of callback decorator that will fire after the e.g. @app.onload(
dash.dependencies.Output('field_select', 'options'),
)
def get_field_options():
return some_db_query() Should I attempt to implement this? What do you guys think? |
Latest build failures are to do with this breaking backwards compatibility 🤷♂️ |
dash/dash.py
Outdated
else: | ||
self.server = Flask(name, static_folder=static_folder) | ||
else: | ||
self.server = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the choice of True
(auto), False
(defer) and Flask(...)
(use existing) but then the default value should be updated to True
. I'd also say, given that we're now accepting multiple value types for server
, that we should explicitly throw an error if we get neither a Flask
nor a bool
- I know @chriddyp likes making specific types in exceptions.py
, but I'd be fine with just something like ValueError
So something like:
if isinstance(server, bool):
self.server = Flask(...) if server else None
elif isinstance(server, Flask):
self.server = server
else:
raise ValueError('server must be a Flask app or a boolean')
re: This could be a nice thing to add, though let's look at the consequences and alternatives a bit first. I can imagine a few potentially confusing aspects here: If If dd_opts = []
app.layout = html.Div(... ... dcc.Dropdown(id='field_select', options=dd_opts), ...)
@app.after_init
def with_db():
# slice mutation rather than extend, just in case this somehow gets run multiple times
dd_opts[:] = some_db_query()
# or a helper to find something in the layout
app.find_in_layout('field_select').options = some_db_query() Anyway given that this is pretty easy using a layout function, I'm not entirely convinced we need to add anything, but curious to hear whether you think this is sufficient. |
Yes, in my use case I ended up using a layout function almost exactly as you've shown (including the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @TMiguelT - looks great!
Closes #738: Support the Flask Extension API
Contributor Checklist
defer_app
flaginit_app()
methodoptionals
CHANGELOG.md