Skip to content

Commit

Permalink
Enable session storage of initial values (#91)
Browse files Browse the repository at this point in the history
* Relocate store and fetch of initial arguments into util.py

* Added flag to use session as an alternative to the cache

* Set default to cache not session for initial arguments

* Set default to cache

* Remove trailing slash

* Add tests of initial argument configuration
  • Loading branch information
delsim authored and GibbsConsulting committed Dec 26, 2018
1 parent f7ed14e commit 09831c8
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 18 deletions.
2 changes: 2 additions & 0 deletions demo/demo/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@
"http_poke_enabled" : True, # Flag controlling availability of direct-to-messaging http endpoint

"view_decorator" : None, # Specify a function to be used to wrap each of the dpd view functions

"cache_arguments" : True, # True for cache, False for session-based argument propagation
}

# Static files (CSS, JavaScript, Images)
Expand Down
13 changes: 2 additions & 11 deletions django_plotly_dash/templatetags/plotly_dash.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,12 @@

# pylint: disable=too-many-arguments, unused-variable, unused-argument, possibly-unused-variable

import uuid

from django import template
from django.core.cache import cache

from django.contrib.sites.shortcuts import get_current_site

from django_plotly_dash.models import DashApp
from django_plotly_dash.util import pipe_ws_endpoint_name, cache_timeout_initial_arguments
from django_plotly_dash.util import pipe_ws_endpoint_name, store_initial_arguments

register = template.Library()

Expand Down Expand Up @@ -74,13 +71,7 @@ def plotly_app(context, name=None, slug=None, da=None, ratio=0.1, use_frameborde
height: 100%;
"""

if initial_arguments:
# Generate a cache id
cache_id = "dpd-initial-args-%s" % str(uuid.uuid4()).replace('-', '')
# Store args in json form in cache
cache.set(cache_id, initial_arguments, cache_timeout_initial_arguments())
else:
cache_id = None
cache_id = store_initial_arguments(context['request'], initial_arguments)

da, app = _locate_daapp(name, slug, da, cache_id=cache_id)

Expand Down
41 changes: 41 additions & 0 deletions django_plotly_dash/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,44 @@ def test_injection_updating(client):
assert response3.status_code == 200

assert response3.content.find(b'Test 789 content') > 0

@pytest.mark.django_db
def test_argument_settings(settings, client):
'Test the setting that controls how initial arguments are propagated through to the dash app'

from django_plotly_dash.util import initial_argument_location, store_initial_arguments, get_initial_arguments

assert initial_argument_location()

settings.PLOTLY_DASH = {'cache_arguments': True}

assert initial_argument_location()

test_value = {"test":"first"}

cache_id = store_initial_arguments(None, test_value)

assert len(cache_id) > 10

fetched = get_initial_arguments(None, cache_id)

assert fetched == test_value

settings.PLOTLY_DASH = {'cache_arguments': False}

assert not initial_argument_location()

cache_id2 = store_initial_arguments(client, test_value)

assert len(cache_id2) > 10

assert cache_id != cache_id2

## For some reason, sessions are continually replaced, so lookup here doesnt work
#fetched2 = get_initial_arguments(client, cache_id2)
#assert fetched2 == test_value

assert store_initial_arguments(None, None) is None
assert get_initial_arguments(None, None) is None
assert store_initial_arguments(client, None) is None
assert get_initial_arguments(client, None) is None
4 changes: 2 additions & 2 deletions django_plotly_dash/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@

for base_type, args, name_prefix, url_ending, name_suffix in [('instance', {}, '', '', '', ),
('app', {'stateless':True}, 'app-', '', '', ),
('instance', {}, '', '/initial/<slug:cache_id>/', '--args', ),
('app', {'stateless':True}, 'app-', '/initial/<slug:cache_id>/', '--args', ),
('instance', {}, '', '/initial/<slug:cache_id>', '--args', ),
('app', {'stateless':True}, 'app-', '/initial/<slug:cache_id>', '--args', ),
]:

for url_part, view_function, name, url_suffix in [('_dash-routes', routes, 'routes', '', ),
Expand Down
38 changes: 38 additions & 0 deletions django_plotly_dash/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
'''

import uuid

from django.conf import settings
from django.core.cache import cache

def _get_settings():
try:
Expand Down Expand Up @@ -57,3 +60,38 @@ def http_poke_endpoint_enabled():
def cache_timeout_initial_arguments():
'Return cache timeout, in seconds, for initial arguments'
return _get_settings().get('cache_timeout_initial_arguments', 60)

def initial_argument_location():
'Return True if cache to be used for setting and getting initial arguments, or False for a session'

setget_location = _get_settings().get('cache_arguments', True)

return setget_location

def store_initial_arguments(request, initial_arguments=None):
'Store initial arguments, if any, and return a cache identifier'

if initial_arguments is None:
return None

# Generate a cache id
cache_id = "dpd-initial-args-%s" % str(uuid.uuid4()).replace('-', '')

# Store args in json form in cache
if initial_argument_location():
cache.set(cache_id, initial_arguments, cache_timeout_initial_arguments())
else:
request.session[cache_id] = initial_arguments

return cache_id

def get_initial_arguments(request, cache_id=None):
'Extract initial arguments for the dash app'

if cache_id is None:
return None

if initial_argument_location():
return cache.get(cache_id)

return request.session[cache_id]
7 changes: 2 additions & 5 deletions django_plotly_dash/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
import json

from django.http import HttpResponse, HttpResponseRedirect
from django.core.cache import cache

from .models import DashApp
from .util import get_initial_arguments

def routes(*args, **kwargs):
'Return routes'
Expand All @@ -52,10 +52,7 @@ def layout(request, ident, stateless=False, cache_id=None, **kwargs):
view_func = app.locate_endpoint_function('dash-layout')
resp = view_func()

if cache_id:
initial_arguments = cache.get(cache_id)
else:
initial_arguments = None
initial_arguments = get_initial_arguments(request, cache_id)

response_data, mimetype = app.augment_initial_layout(resp, initial_arguments)
return HttpResponse(response_data,
Expand Down
11 changes: 11 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ below.
# Name of view wrapping function
"view_decorator": None,
# Flag to control location of initial argument storage
"cache_arguments": True,
}
Defaults are inserted for missing values. It is also permissible to not have any ``PLOTLY_DASH`` entry in
Expand Down Expand Up @@ -72,4 +75,12 @@ To restrict all access to logged-in users, use the ``login_required`` wrapper:
More information can be found in the :ref:`view decoration <access_control>` section.

.. _cache_arguments:

Initial arguments
-----------------

Initial arguments are stored within the server between the specification of an app in a template tag and the invocation of the
view functions for the app. This storage is transient and can be efficiently performed using Django's caching framework. In some
situations, however, a suitably configured cache is not available. For this use case, setting the ``cache_arguments`` flag to ``False`` will
cause initial arguments to be placed inside the Django session.

0 comments on commit 09831c8

Please sign in to comment.