Skip to content

Commit 5a59976

Browse files
author
Mark Gibbs
committed
Refactor code and add documentation
1 parent 280f9dc commit 5a59976

File tree

13 files changed

+84
-30
lines changed

13 files changed

+84
-30
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@ Expose [plotly dash](https://plot.ly/products/dash/) apps as django tags.
55
See the source for this project here:
66
<https://github.com/GibbsConsulting/django-plotly-dash>
77

8-
Online documentation can be found here:
8+
This README file provides a short guide to installing and using the package, and also
9+
outlines how to run the demonstration application.
10+
11+
More detailed information
12+
can be found in the online documentation at
913
<https://readthedocs.org/projects/django-plotly-dash>
1014

15+
1116
## Installation
1217

1318
First, install the package. This will also install plotly and some dash packages if they are not already present.

dev_requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ dash-html-components==0.10.1
1010
dash-renderer==0.12.1
1111
decorator==4.3.0
1212
Django==2.0.5
13-
-e git+https://github.com/delsim/django-plotly-dash.git@293b454ee8965422155a971d9e417878dc89ce82#egg=django_plotly_dash
1413
docopt==0.6.2
1514
docutils==0.14
1615
Flask==1.0.2

django_plotly_dash/app_name.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
app_name = "the_django_plotly_dash"

django_plotly_dash/dash_wrapper.py

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,44 @@
33

44
from django.urls import reverse
55

6+
from .app_name import app_name
7+
68
uid_counter = 0
79

810
usable_apps = {}
911
app_instances = {}
1012
nd_apps = {}
1113

1214
def get_app_by_name(name):
15+
'''
16+
Locate a registered dash app by name, and return a DelayedDash instance encapsulating the app.
17+
'''
1318
return usable_apps.get(name,None)
1419

1520
def get_app_instance_by_id(id):
21+
'''
22+
Locate an instance of a dash app by identifier, or return None if one does not exist
23+
'''
1624
return nd_apps.get(id,None)
1725

26+
def get_or_form_app(id, name, **kwargs):
27+
'''
28+
Locate an instance of a dash app by identifier, loading or creating a new instance if needed
29+
'''
30+
app = get_app_instance_by_id(id)
31+
if app:
32+
return app
33+
dd = get_app_by_name(name)
34+
return dd.form_dash_instance()
35+
36+
class Holder:
37+
def __init__(self):
38+
self.items = []
39+
def append_css(self, stylesheet):
40+
self.items.append(stylesheet)
41+
def append_script(self, script):
42+
self.items.append(script)
43+
1844
class DelayedDash:
1945
def __init__(self, name=None, **kwargs):
2046
if name is None:
@@ -24,27 +50,27 @@ def __init__(self, name=None, **kwargs):
2450
else:
2551
self._uid = name
2652
self.layout = None
27-
self._rep_dash = None
2853
self._callback_sets = []
2954

55+
self.css = Holder()
56+
self.scripts = Holder()
57+
3058
global usable_apps
3159
usable_apps[self._uid] = self
3260

33-
def _RepDash(self):
34-
if self._rep_dash is None:
35-
self._rep_dash = self._form_repdash()
36-
return self._rep_dash
37-
38-
def _form_repdash(self):
61+
def form_dash_instance(self):
3962
rd = NotDash(name_root=self._uid,
40-
app_pathname="django_plotly_dash:main")
63+
app_pathname="%s:main" % app_name)
4164
rd.layout = self.layout
65+
4266
for cb, func in self._callback_sets:
4367
rd.callback(**cb)(func)
44-
return rd
68+
for s in self.css.items:
69+
rd.css.append_css(s)
70+
for s in self.scripts.items:
71+
rd.scripts.append_script(s)
4572

46-
def base_url(self):
47-
return self._RepDash().base_url()
73+
return rd
4874

4975
def callback(self, output, inputs=[], state=[], events=[]):
5076
callback_set = {'output':output,
@@ -92,12 +118,12 @@ def __init__(self, name_root, app_pathname, **kwargs):
92118

93119
kwargs['url_base_pathname'] = self._base_pathname
94120
kwargs['server'] = self._notflask
121+
95122
super(NotDash, self).__init__(**kwargs)
96123
global nd_apps
97124
nd_apps[self._uid] = self
98-
if False: # True for some debug info and a load of errors...
99-
self.css.config.serve_locally = True
100-
self.scripts.config.serve_locally = True
125+
126+
self._adjust_id = False
101127

102128
def flask_app(self):
103129
return self._flask_app
@@ -123,10 +149,13 @@ def locate_endpoint_function(self, name=None):
123149

124150
@Dash.layout.setter
125151
def layout(self, value):
126-
self._fix_component_id(value)
152+
153+
if self._adjust_id:
154+
self._fix_component_id(value)
127155
return Dash.layout.fset(self, value)
128156

129157
def _fix_component_id(self, component):
158+
130159
theID = getattr(component,"id",None)
131160
if theID is not None:
132161
setattr(component,"id",self._fix_id(theID))
@@ -137,6 +166,8 @@ def _fix_component_id(self, component):
137166
pass
138167

139168
def _fix_id(self, name):
169+
if not self._adjust_id:
170+
return name
140171
return "%s_-_%s" %(self._uid,
141172
name)
142173

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
<div>
2-
<iframe src="{{url}}"></iframe>
2+
<iframe src="{{app.base_url}}"></iframe>
33
</div>

django_plotly_dash/templatetags/plotly_dash.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22

33
register = template.Library()
44

5-
from django_plotly_dash.dash_wrapper import get_app_by_name
5+
from django_plotly_dash.dash_wrapper import get_or_form_app
66

77
@register.inclusion_tag("django_plotly_dash/plotly_item.html", takes_context=True)
88
def plotly_item(context, app_name):
99

10-
app = get_app_by_name(app_name)
11-
url = app.base_url()
10+
app = get_or_form_app(app_name, app_name)
1211

1312
return locals()

django_plotly_dash/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from .views import routes, layout, dependencies, update, main_view
55

6-
app_name = "django_plotly_dash"
6+
from .app_name import app_name
77

88
urlpatterns = [
99
path('<slug:id>_dash-routes', routes, name="routes"),

django_plotly_dash/views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import flask
44
import json
55

6-
from .dash_wrapper import get_app_instance_by_id
6+
from .dash_wrapper import get_app_instance_by_id, get_or_form_app
77
from django.http import HttpResponse
88

99
def routes(*args,**kwargs):
@@ -37,7 +37,7 @@ def update(request, id, **kwargs):
3737
content_type=resp.mimetype)
3838

3939
def main_view(request, id, **kwargs):
40-
app = get_app_instance_by_id(id)
40+
app = get_or_form_app(id, id)
4141
mFunc = app.locate_endpoint_function()
4242
resp = mFunc()
4343
return HttpResponse(resp)

docs/index.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@
33
You can adapt this file completely to your liking, but it should at least
44
contain the root `toctree` directive.
55
6-
Welcome to django-plotly-dash's documentation!
7-
==============================================
6+
django-plotly-dash
7+
==================
8+
9+
`Plotly Dash <https://dash.plot.ly/>`_ applications served up in Django templates using tags.
810

911
Contents
1012
--------
1113

1214
.. toctree::
1315
:maxdepth: 2
1416

17+
introduction
1518
installation
1619
simple_use
1720

docs/introduction.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.. _introduction:
2+
3+
Introduction
4+
============
5+
6+
The purpose of django-plotly-dash is to enable Plotly Dash applications to be served up as part of a Django application, in order to provide
7+
these features:
8+
9+
* Multiple dash applications can be used on a single page
10+
* Separate instances of a dash application can persist along with internal state
11+
* Leverage user management and access control and other parts of the Django infrastructure
12+
* Consolidate into a single server process to simplify scaling
13+
14+
There is nothing here that cannot be achieved through expanding the Flask app around Plotly Dash, or indeed by using an alternative web
15+
framework. The purpose of this project is to enable the above features, given that the choice to use Django has already been made.

0 commit comments

Comments
 (0)