Skip to content

Commit a47bd76

Browse files
author
Mark Gibbs
committed
Refactored app loading and refreshed documentation
1 parent f4e28af commit a47bd76

File tree

8 files changed

+82
-100
lines changed

8 files changed

+82
-100
lines changed

README.md

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -88,21 +88,6 @@ def callback_color(dropdown_value):
8888
def callback_size(dropdown_color, dropdown_size):
8989
return "The chosen T-shirt is a %s %s one." %(dropdown_size,
9090
dropdown_color)
91-
92-
a2 = DelayedDash("Ex2")
93-
a2.layout = html.Div([
94-
dcc.RadioItems(id="dropdown-one",options=[{'label':i,'value':j} for i,j in [
95-
("O2","Oxygen"),("N2","Nitrogen"),]
96-
],value="Oxygen"),
97-
html.Div(id="output-one")
98-
])
99-
100-
@a2.callback(
101-
dash.dependencies.Output('output-one','children'),
102-
[dash.dependencies.Input('dropdown-one','value')]
103-
)
104-
def callback_c(*args,**kwargs):
105-
return "Args are %s and kwargs are %s" %("".join(*args),str(kwargs))
10691
```
10792

10893
Note that the `DelayedDash` constructor requires a name to be specified. This name is then used to identify the dash app in
@@ -111,7 +96,7 @@ templates:
11196
```jinja2
11297
{% load plotly_dash %}
11398
114-
{% plotly_item "SimpleExample" %}
99+
{% plotly_item name="SimpleExample" %}
115100
```
116101

117102
The registration code needs to be in a location

demo/demo/templates/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
<body>
66
<div>
77
Content here
8-
{%plotly_item "simpleexample-1" ratio=0.2 %}
8+
{%plotly_app slug="simpleexample-1" ratio=0.2 %}
99
</div>
1010
<div>
1111
Content here
12-
{%plotly_item "SimpleExample"%}
12+
{%plotly_app name="SimpleExample"%}
1313
</div>
1414
<div>
1515
Content here
16-
{%plotly_item "Ex2"%}
16+
{%plotly_app name="Ex2"%}
1717
</div>
1818
</body>
1919
</html>

django_plotly_dash/dash_wrapper.py

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,43 +13,17 @@
1313
uid_counter = 0
1414

1515
usable_apps = {}
16-
nd_apps = {}
1716

1817
def add_usable_app(name, app):
1918
global usable_apps
2019
usable_apps[name] = app
2120

22-
def add_instance(id, instance):
23-
global nd_apps
24-
nd_apps[id] = instance
25-
26-
def get_app_by_name(name):
21+
def get_stateless_by_name(name):
2722
'''
2823
Locate a registered dash app by name, and return a DelayedDash instance encapsulating the app.
2924
'''
30-
return usable_apps.get(name,None)
31-
32-
def get_app_instance_by_id(id):
33-
'''
34-
Locate an instance of a dash app by identifier, or return None if one does not exist
35-
'''
36-
return nd_apps.get(id,None)
37-
38-
def clear_app_instance(id):
39-
try:
40-
del nd_apps[id]
41-
except:
42-
pass
43-
44-
def get_or_form_app(id, name, **kwargs):
45-
'''
46-
Locate an instance of a dash app by identifier, loading or creating a new instance if needed
47-
'''
48-
app = get_app_instance_by_id(id)
49-
if app:
50-
return app
51-
dd = get_app_by_name(name)
52-
return dd.form_dash_instance()
25+
# TODO wrap this in raising a 404 if not found
26+
return usable_apps[name]
5327

5428
class Holder:
5529
def __init__(self):
@@ -78,9 +52,20 @@ def __init__(self, name=None, **kwargs):
7852

7953
self._expanded_callbacks = False
8054

55+
def as_dash_instance(self):
56+
'''
57+
Form a dash instance, for stateless use of this app
58+
'''
59+
return self.form_dash_instance()
60+
8161
def form_dash_instance(self, replacements=None, specific_identifier=None):
62+
if not specific_identifier:
63+
app_pathname = "%s:app-%s"% (app_name, main_view_label)
64+
else:
65+
app_pathname="%s:%s" % (app_name, main_view_label)
66+
8267
rd = NotDash(name_root=self._uid,
83-
app_pathname="%s:%s" % (app_name, main_view_label),
68+
app_pathname=app_pathname,
8469
expanded_callbacks = self._expanded_callbacks,
8570
replacements = replacements,
8671
specific_identifier = specific_identifier)
@@ -134,8 +119,6 @@ def __init__(self, name_root, app_pathname=None, replacements = None, specific_i
134119
else:
135120
self._uid = name_root
136121

137-
add_instance(self._uid, self)
138-
139122
self._flask_app = Flask(self._uid)
140123
self._notflask = NotFlask()
141124
self._base_pathname = reverse(app_pathname,kwargs={'id':self._uid})
@@ -295,4 +278,3 @@ def dispatch_with_args(self, body, argMap):
295278

296279
return self.callback_map[target_id]['callback'](*args,**argMap)
297280

298-

django_plotly_dash/models.py

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from django.contrib import admin
33
from django.utils.text import slugify
44

5-
from .dash_wrapper import get_app_instance_by_id, get_app_by_name, clear_app_instance
5+
from .dash_wrapper import get_stateless_by_name
66

77
import json
88

@@ -27,46 +27,25 @@ def save(self, *args, **kwargs):
2727
if not self.slug or len(self.slug) < 2:
2828
self.slug = slugify(self.instance_name)
2929
super(DashApp, self).save(*args,**kwargs)
30-
# TODO at this point should invaliate any local cache of the older values
31-
clear_app_instance(self.slug)
30+
31+
def _stateless_dash_app(self):
32+
# TODO make this a property of the object
33+
dd = get_stateless_by_name(self.app_name)
34+
return dd
3235

3336
def as_dash_instance(self):
34-
ai = get_app_instance_by_id(self.slug)
35-
if ai:
36-
return ai
37-
dd = get_app_by_name(self.app_name)
37+
dd = self._stateless_dash_app()
3838
base = json.loads(self.base_state)
3939
return dd.form_dash_instance(replacements=base,
4040
specific_identifier=self.slug)
4141

42-
@staticmethod
43-
def get_app_instance(id):
44-
'''
45-
Locate an application instance by id, either in local cache or in Database.
46-
If in neither, then create a new instance assuming that the id is the app name.
47-
'''
48-
local_instance = get_app_instance_by_id(id)
49-
if local_instance:
50-
return local_instance
51-
try:
52-
return DashApp.objects.get(slug=id).as_dash_instance()
53-
except:
54-
pass
55-
56-
# Really no luck at all!
57-
dd = get_app_by_name(id)
58-
return dd.form_dash_instance()
59-
6042
def _get_base_state(self):
6143
'''
6244
Get the base state of the object, as defined by the app.layout code, as a python dict
6345
'''
64-
# Get base layout response, from a base object
65-
base_app_inst = get_app_instance_by_id(self.app_name)
66-
if not base_app_inst:
67-
base_app = get_app_by_name(self.app_name)
68-
base_app_inst = base_app.form_dash_instance()
46+
base_app_inst = self._stateless_dash_app()
6947

48+
# Get base layout response, from a base object
7049
base_resp = base_app_inst.locate_endpoint_function('dash-layout')()
7150

7251
base_obj = json.loads(base_resp.data.decode('utf-8'))

django_plotly_dash/templatetags/plotly_dash.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from django import template
2+
from django.shortcuts import get_object_or_404
23

34
register = template.Library()
45

56
from django_plotly_dash.models import DashApp
7+
from django_plotly_dash.dash_wrapper import get_stateless_by_name
68

79
@register.inclusion_tag("django_plotly_dash/plotly_item.html", takes_context=True)
8-
def plotly_item(context, app_name, ratio=0.1, use_frameborder=False):
10+
def plotly_app(context, name=None, slug=None, ratio=0.1, use_frameborder=False):
911

1012
fbs = use_frameborder and '1' or '0'
1113

@@ -24,6 +26,13 @@ def plotly_item(context, app_name, ratio=0.1, use_frameborder=False):
2426
height: 100%;
2527
"""
2628

27-
app = DashApp.get_app_instance(app_name)
29+
if name is not None:
30+
da = get_stateless_by_name(name)
31+
app = da.form_dash_instance()
32+
33+
if slug is not None:
34+
da = get_object_or_404(DashApp,slug=slug)
35+
app = da.as_dash_instance()
2836

2937
return locals()
38+

django_plotly_dash/urls.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
from .app_name import app_name, main_view_label
77

88
urlpatterns = [
9-
path('<slug:id>_dash-routes', routes, name="routes"),
10-
path('<slug:id>_dash-layout', layout, name="layout"),
11-
path('<slug:id>_dash-dependencies', dependencies, name="dependencies"),
12-
path('<slug:id>_dash-update-component', csrf_exempt(update), name="update-component"),
9+
path('instance/<slug:id>_dash-routes', routes, name="routes"),
10+
path('instance/<slug:id>_dash-layout', layout, name="layout"),
11+
path('instance/<slug:id>_dash-dependencies', dependencies, name="dependencies"),
12+
path('instance/<slug:id>_dash-update-component', csrf_exempt(update), name="update-component"),
13+
path('instance/<slug:id>', main_view, name=main_view_label),
1314

14-
path('<slug:id>', main_view, name=main_view_label),
15+
path('app/<slug:id>_dash-routes', routes, {'stateless':True}, name="app-routes"),
16+
path('app/<slug:id>_dash-layout', layout, {'stateless':True}, name="app-layout"),
17+
path('app/<slug:id>_dash-dependencies', dependencies, {'stateless':True}, name="app-dependencies"),
18+
path('app/<slug:id>_dash-update-component', csrf_exempt(update), {'stateless':True}, name="app-update-component"),
19+
path('app/<slug:id>', main_view, {'stateless':True}, name='app-%s'%main_view_label),
1520
]
1621

django_plotly_dash/views.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,46 @@
1-
from django.shortcuts import render
1+
from django.shortcuts import render, get_object_or_404
22
from django.http import HttpResponse
33

44
import json
55

66
from .models import DashApp
7+
from .dash_wrapper import get_stateless_by_name
78

89
def routes(*args,**kwargs):
9-
pass
10+
raise NotImplementedError
11+
12+
def dependencies(request, id, stateless=False, **kwargs):
13+
if stateless:
14+
da = get_stateless_by_name(id)
15+
else:
16+
da = get_object_or_404(DashApp,slug=id)
17+
18+
app = da.as_dash_instance()
1019

11-
def dependencies(request, id, **kwargs):
12-
app = DashApp.get_app_instance(id)
1320
with app.app_context():
1421
mFunc = app.locate_endpoint_function('dash-dependencies')
1522
resp = mFunc()
1623
return HttpResponse(resp.data,
1724
content_type=resp.mimetype)
1825

19-
def layout(request, id, **kwargs):
20-
app = DashApp.get_app_instance(id)
26+
def layout(request, id, stateless=False, **kwargs):
27+
if stateless:
28+
da = get_stateless_by_name(id)
29+
else:
30+
da = get_object_or_404(DashApp,slug=id)
31+
app = da.as_dash_instance()
32+
2133
mFunc = app.locate_endpoint_function('dash-layout')
2234
resp = mFunc()
2335
return app.augment_initial_layout(resp)
2436

25-
def update(request, id, **kwargs):
26-
app = DashApp.get_app_instance(id)
37+
def update(request, id, stateless=False, **kwargs):
38+
if stateless:
39+
da = get_stateless_by_name(id)
40+
else:
41+
da = get_object_or_404(DashApp,slug=id)
42+
app = da.as_dash_instance()
43+
2744
rb = json.loads(request.body.decode('utf-8'))
2845

2946
if app.use_dash_dispatch():
@@ -44,8 +61,13 @@ def update(request, id, **kwargs):
4461
return HttpResponse(resp.data,
4562
content_type=resp.mimetype)
4663

47-
def main_view(request, id, **kwargs):
48-
app = DashApp.get_app_instance(id)
64+
def main_view(request, id, stateless=False, **kwargs):
65+
if stateless:
66+
da = get_stateless_by_name(id)
67+
else:
68+
da = get_object_or_404(DashApp,slug=id)
69+
app = da.as_dash_instance()
70+
4971
mFunc = app.locate_endpoint_function()
5072
resp = mFunc()
5173
return HttpResponse(resp)

docs/simple_use.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ templates:::
5252

5353
{%load plotly_dash%}
5454

55-
{%plotly_item "SimpleExample"%}
55+
{%plotly_item name="SimpleExample"%}
5656

5757
The registration code needs to be in a location
5858
that will be imported into the Django process before any model or

0 commit comments

Comments
 (0)