Skip to content

Commit eae96bb

Browse files
author
Mark Gibbs
committed
Added DashApp model with associated migration
1 parent a2db2fb commit eae96bb

File tree

12 files changed

+148
-31
lines changed

12 files changed

+148
-31
lines changed

demo/configdb.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env python
2+
#
3+
# Create a new superuser
4+
from django.contrib.auth import get_user_model
5+
6+
UserModel = get_user_model()
7+
8+
name="admin"
9+
password="admin"
10+
11+
try:
12+
UserModel.objects.get(username=name)
13+
except:
14+
su = UserModel.objects.create_user(name,password=password)
15+
su.is_staff=True
16+
su.is_superuser=True
17+
su.save()

demo/demo/templates/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<body>
66
<div>
77
Content here
8-
{%plotly_item "SimpleExample"%}
8+
{%plotly_item "simpleexample-1"%}
99
</div>
1010
<div>
1111
Content here

django_plotly_dash/admin.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
from django.contrib import admin
22

3-
# Register your models here.
3+
from .models import DashApp, DashAppAdmin
4+
5+
admin.site.register(DashApp, DashAppAdmin)
6+

django_plotly_dash/app_name.py

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

django_plotly_dash/dash_wrapper.py

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,21 @@
88

99
from plotly.utils import PlotlyJSONEncoder
1010

11-
from .app_name import app_name
11+
from .app_name import app_name, main_view_label
1212

1313
uid_counter = 0
1414

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

18+
def add_usable_app(name, app):
19+
global usable_apps
20+
usable_apps[name] = app
21+
22+
def add_instance(id, instance):
23+
global nd_apps
24+
nd_apps[id] = instance
25+
1926
def get_app_by_name(name):
2027
'''
2128
Locate a registered dash app by name, and return a DelayedDash instance encapsulating the app.
@@ -28,6 +35,9 @@ def get_app_instance_by_id(id):
2835
'''
2936
return nd_apps.get(id,None)
3037

38+
def clear_app_instance(id):
39+
del nd_apps[id]
40+
3141
def get_or_form_app(id, name, **kwargs):
3242
'''
3343
Locate an instance of a dash app by identifier, loading or creating a new instance if needed
@@ -60,15 +70,17 @@ def __init__(self, name=None, **kwargs):
6070
self.css = Holder()
6171
self.scripts = Holder()
6272

63-
global usable_apps
64-
usable_apps[self._uid] = self
73+
add_usable_app(self._uid,
74+
self)
6575

6676
self._expanded_callbacks = False
6777

68-
def form_dash_instance(self):
78+
def form_dash_instance(self, replacements=None, specific_identifier=None):
6979
rd = NotDash(name_root=self._uid,
70-
app_pathname="%s:main" % app_name,
71-
expanded_callbacks = self._expanded_callbacks)
80+
app_pathname="%s:%s" % (app_name, main_view_label),
81+
expanded_callbacks = self._expanded_callbacks,
82+
replacements = replacements,
83+
specific_identifier = specific_identifier)
7284
rd.layout = self.layout
7385

7486
for cb, func in self._callback_sets:
@@ -112,17 +124,14 @@ def run(self,*args,**kwargs):
112124
pass
113125

114126
class NotDash(Dash):
115-
def __init__(self, name_root, app_pathname=None, replacements = None, **kwargs):
127+
def __init__(self, name_root, app_pathname=None, replacements = None, specific_identifier=None, expanded_callbacks=False, **kwargs):
116128

117-
global app_instances
118-
current_instances = app_instances.get(name_root,None)
119-
120-
if current_instances is not None:
121-
self._uid = "%s-%i" % (name_root,len(current_instances)+1)
122-
current_instances.append(self)
129+
if specific_identifier is not None:
130+
self._uid = specific_identifier
123131
else:
124132
self._uid = name_root
125-
app_instances[name_root] = [self,]
133+
134+
add_instance(self._uid, self)
126135

127136
self._flask_app = Flask(self._uid)
128137
self._notflask = NotFlask()
@@ -132,11 +141,9 @@ def __init__(self, name_root, app_pathname=None, replacements = None, **kwargs):
132141
kwargs['server'] = self._notflask
133142

134143
super(NotDash, self).__init__(**kwargs)
135-
global nd_apps
136-
nd_apps[self._uid] = self
137144

138145
self._adjust_id = False
139-
self._dash_dispatch = not kwargs.get('expanded_callbacks',False)
146+
self._dash_dispatch = not expanded_callbacks
140147
if replacements:
141148
self._replacements = replacements
142149
else:
@@ -164,6 +171,8 @@ def augment_initial_layout(self, base_response):
164171
content_type=base_response.mimetype)
165172

166173
def walk_tree_and_replace(self, data):
174+
# Walk the tree. Rely on json decoding to insert instances of dict and list
175+
# ie we use a dna test for anatine, rather than our eyes and ears...
167176
if isinstance(data,dict):
168177
response = {}
169178
replacements = {}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Generated by Django 2.0.5 on 2018-05-10 21:48
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
initial = True
9+
10+
dependencies = [
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name='DashApp',
16+
fields=[
17+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18+
('app_name', models.CharField(max_length=100)),
19+
('instance_name', models.CharField(blank=True, max_length=100, unique=True)),
20+
('slug', models.SlugField(blank=True, max_length=110, unique=True)),
21+
('base_state', models.TextField()),
22+
('creation', models.DateTimeField(auto_now_add=True)),
23+
('update', models.DateTimeField(auto_now=True)),
24+
],
25+
),
26+
]

django_plotly_dash/migrations/__init__.py

Whitespace-only changes.

django_plotly_dash/models.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,63 @@
11
from django.db import models
2+
from django.contrib import admin
3+
from django.utils.text import slugify
4+
5+
from .dash_wrapper import get_app_instance_by_id, get_app_by_name, clear_app_instance
6+
7+
import json
8+
9+
class DashApp(models.Model):
10+
'''
11+
An instance of this model represents a dash application and its internal state
12+
'''
13+
app_name = models.CharField(max_length=100, blank=False, null=False, unique=False)
14+
instance_name = models.CharField(max_length=100, unique=True, blank=True, null=False)
15+
slug = models.SlugField(max_length=110, unique=True, blank=True)
16+
base_state = models.TextField(null=False) # If mandating postgresql then this could be a JSONField
17+
creation = models.DateTimeField(auto_now_add=True)
18+
update = models.DateTimeField(auto_now=True)
19+
20+
def __str__(self):
21+
return self.instance_name
22+
23+
def save(self, *args, **kwargs):
24+
if not self.instance_name:
25+
existing_count = DashApp.objects.filter(app_name=self.app_name).count()
26+
self.instance_name = "%s-%i" %(self.app_name, existing_count+1)
27+
if not self.slug or len(self.slug) < 2:
28+
self.slug = slugify(self.instance_name)
29+
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)
32+
33+
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)
38+
base = json.loads(self.base_state)
39+
return dd.form_dash_instance(replacements=base,
40+
specific_identifier=self.slug)
41+
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+
60+
class DashAppAdmin(admin.ModelAdmin):
61+
list_display = ['app_name','instance_name','slug','creation','update',]
62+
list_filter = ['app_name','creation','update',]
263

3-
# Create your models here.

django_plotly_dash/templatetags/plotly_dash.py

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

33
register = template.Library()
44

5-
from django_plotly_dash.dash_wrapper import get_or_form_app
5+
from django_plotly_dash.models import DashApp
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_or_form_app(app_name, app_name)
10+
app = DashApp.get_app_instance(app_name)
1111

1212
return locals()

django_plotly_dash/urls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33

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

6-
from .app_name import app_name
6+
from .app_name import app_name, main_view_label
77

88
urlpatterns = [
99
path('<slug:id>_dash-routes', routes, name="routes"),
1010
path('<slug:id>_dash-layout', layout, name="layout"),
1111
path('<slug:id>_dash-dependencies', dependencies, name="dependencies"),
1212
path('<slug:id>_dash-update-component', csrf_exempt(update), name="update-component"),
1313

14-
path('<slug:id>', main_view, name="main"),
14+
path('<slug:id>', main_view, name=main_view_label),
1515
]
1616

0 commit comments

Comments
 (0)