I have just upgraded to dataiku 9.0, and I am trying out the new Dash integration. It's great! But since the Dash object is instantiated by dataiku, I am not sure how to pass arguments to the constructor, e.g. like this
app = dash.Dash(external_stylesheets=["some_stylesheet.css"], prevent_initial_callbacks=True)
What is the recommended way? 🙂
We are glad you like the new release 🙂
The new Dash Webapp has been built for simple use cases running natively on our platform. To achieve what you want to do, you can use the following workaround:
app.config.external_stylesheets =["some_stylesheet.css"] app.config.prevent_initial_callbacks = True
However, some of the attributes can only be set through the constructor, and there are unfortunately no workaround to set them once the object has been initialised:
[ "name", "assets_folder", "assets_url_path", "eager_loading", "url_base_pathname", "routes_pathname_prefix", "requests_pathname_prefix", "serve_locally", "compress", ]
Do not hesitate if you have more question about it!
Have a nice day,
Hi @HenriC ,
thank you for the answer! Unfortunately, one of the properties that I need to modify is the asserts folder (I didn't include it in my initial question to keep the example as simple as possible). Is there a reason why you don't allow editing these properties?
In Dataiku 8.0 I was able to do it simply by binding the Dash app to the flask webserver of a "normal" web app. It required some hacking though, so I would prefer moving to the new native integration, which is much cleaner. But I need to be able to load my own assets 🙂
The reason is quite simple. To make our app working out of the box, we intensiate `app = Dash(...)` just before running the user code. Once this app is created and linked to the flask server, well it can't be unregistered from it.
The list of fields comes from Dash source code: dash.py:335. This list describes all fields that can't be set outside the constructor, and is unfortunately not up to us. Does it make sense?
My guess would be for you to do as you were doing in DSS 8.0. We know it is not optimal and we will take your request in consideration for the coming releases 🙂
Would it be possible to move that line,
`app = Dash(...)`
into the user code? If you make it part of the templates, it shouldn't make it more complex for "non-power user", and it would unleash the full power of Dash for "power users"?
I have now figured out a hack. So in dataiku 9.0, all Dash apps are linked to the same assets directory. The idea of the hack is to create a subdirectory within this director, and load only assets from this directory. The resulting syntax is,
import my_dash_app from dash_extensions.dataiku import bind_assets_folder assets_folder = os.path.join(os.path.dirname(my_dash_app.__file__), "assets") bind_assets_folder(app, "my_dash_app", assets_folder)
where my_dash_app is the module in which the app is defined, and where the app assets are located in an "assets" folder next to this file (which is the common pattern for dash applications). I think this more-or-less OK, but the hack itself is pretty ugly,
def bind_assets_folder(app, app_id, assets_folder): import os import re from distutils.dir_util import copy_tree # Create assets container folder for the app. dst_assets_folder = os.path.join(app.config.assets_folder, app_id) os.makedirs(dst_assets_folder, exist_ok=True) # Copy assets. copy_tree(assets_folder, dst_assets_folder) def _walk_assets_directory(self): walk_dir = os.path.join(self.config.assets_folder, app_id) # EMHER: Use application sub dir slash_splitter = re.compile(r"[\\/]+") ignore_str = self.config.assets_ignore ignore_filter = re.compile(ignore_str) if ignore_str else None for current, _, files in sorted(os.walk(walk_dir)): if current == walk_dir: base = "" else: s = current.replace(walk_dir, "").lstrip("\\").lstrip("/") splitted = slash_splitter.split(s) if len(splitted) > 1: base = "/".join(slash_splitter.split(s)) else: base = splitted base = os.path.join(base, app_id) # EMHER: Use application sub dir if ignore_filter: files_gen = (x for x in files if not ignore_filter.search(x)) else: files_gen = files for f in sorted(files_gen): path = "/".join([base, f]) if base else f full = os.path.join(current, f) if f.endswith("js"): self.scripts.append_script(self._add_assets_resource(path, full)) elif f.endswith("css"): self.css.append_css(self._add_assets_resource(path, full)) elif f == "favicon.ico": self._favicon = path app._walk_assets_directory = lambda x=app: _walk_assets_directory(app)
@HenriC As a side note, many of the features of [dash-extensions](https://github.com/thedirtyfew/dash-extensions) relies on the app object construction being part of the user code. So to be able to utilize these tools (without excessive hacking), moving the app construction into user code would also be a great benefit 🙂