Skip to content

Commit ecdbda3

Browse files
authored
Merge pull request #6 from bokeh/support_url_args
URL args support and example
2 parents fe63a7e + b2c3a0b commit ecdbda3

File tree

6 files changed

+53
-20
lines changed

6 files changed

+53
-20
lines changed

bokeh_django/__init__.py

+16
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,19 @@
99

1010
import_required("django", "django is required by bokeh-django")
1111
import_required("channels", "The package channels is required by bokeh-django and must be installed")
12+
13+
14+
def with_request(f):
15+
def wrapper(doc):
16+
return f(doc, doc.session_context.request)
17+
return wrapper
18+
19+
20+
def with_url_args(handler):
21+
def wrapper(doc):
22+
request = doc.session_context.request
23+
args = request.url_route['args']
24+
kwargs = request.url_route['kwargs']
25+
return handler(doc, *args, **kwargs)
26+
27+
return wrapper

bokeh_django/routing.py

+5-9
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010
# -----------------------------------------------------------------------------
1111
from __future__ import annotations
1212

13-
import logging # isort:skip
13+
import logging # isort:skip
1414
log = logging.getLogger(__name__)
1515

1616
# -----------------------------------------------------------------------------
1717
# Imports
1818
# -----------------------------------------------------------------------------
1919

2020
# Standard library imports
21-
import re
2221
from pathlib import Path
2322
from typing import Callable, List, Union
2423
import weakref
@@ -192,18 +191,15 @@ def get_websocket_urlpatterns(self) -> List[URLPattern]:
192191
def _add_new_routing(self, routing: Routing) -> None:
193192
kwargs = dict(app_context=routing.app_context)
194193

195-
def join(*components):
196-
return "/".join([component.strip("/") for component in components if component])
197-
198194
def urlpattern(suffix=""):
199-
return r"^{}$".format(join(re.escape(routing.url)) + suffix)
195+
return f"^{routing.url.strip('^$/')}{suffix}$"
200196

201197
if routing.document:
202-
self._http_urlpatterns.append(re_path(urlpattern(), DocConsumer.as_asgi(), kwargs=kwargs))
198+
self._http_urlpatterns.append(re_path(urlpattern(), DocConsumer.as_asgi(**kwargs)))
203199
if routing.autoload:
204-
self._http_urlpatterns.append(re_path(urlpattern("/autoload.js"), AutoloadJsConsumer.as_asgi(), kwargs=kwargs))
200+
self._http_urlpatterns.append(re_path(urlpattern("/autoload.js"), AutoloadJsConsumer.as_asgi(**kwargs)))
205201

206-
self._websocket_urlpatterns.append(re_path(urlpattern("/ws"), WSConsumer.as_asgi(), kwargs=kwargs))
202+
self._websocket_urlpatterns.append(re_path(urlpattern("/ws"), WSConsumer.as_asgi(**kwargs)))
207203

208204
# -----------------------------------------------------------------------------
209205
# Dev API

examples/django_embed/django_embed/shape_viewer.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def _get_coords(self):
2020
def view(self):
2121
return self.figure
2222

23+
2324
class Circle(Shape):
2425

2526
n = param.Integer(default=100, precedence=-1)
@@ -37,6 +38,7 @@ def update(self):
3738
xs, ys = self._get_coords()
3839
self.renderer.data_source.data.update({'x': xs, 'y': ys})
3940

41+
4042
class NGon(Circle):
4143

4244
n = param.Integer(default=3, bounds=(3, 10), precedence=1)
@@ -46,8 +48,10 @@ def update(self):
4648
xs, ys = self._get_coords()
4749
self.renderer.data_source.data.update({'x': xs, 'y': ys})
4850

51+
4952
shapes = [NGon(), Circle()]
5053

54+
5155
class ShapeViewer(param.Parameterized):
5256

5357
shape = param.ObjectSelector(default=shapes[0], objects=shapes)
@@ -78,4 +82,8 @@ def panel(self):
7882

7983

8084
def shape_viewer():
81-
return ShapeViewer().panel()
85+
shapes = [NGon(), Circle()]
86+
viewer = ShapeViewer()
87+
viewer.param.shape.objects = shapes
88+
viewer.shape = shapes[0]
89+
return viewer.panel()

examples/django_embed/django_embed/templates/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ <h2>Autoload Apps</h2>
3131
<li><a href="./sea-surface-temp">Sea Surface Temp</a></li>
3232
<li><a href="./my-sea-surface">Sea Surface Temp</a> (mapped to custom Bokeh handler URI)</li>
3333
<li><a href="./shapes">Shapes (Panel app)</a></li>
34+
<li><a href="./shapes/custom-arg1/custom-arg2">Shapes</a> (with URL variables)</li>
3435
</ul>
3536

3637
</body>

examples/django_embed/django_embed/urls.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
path("sea-surface-temp", views.sea_surface),
3030
path("my-sea-surface", views.sea_surface_custom_uri),
3131
path("shapes", views.shapes),
32+
path("shapes/<str:arg1>/<str:arg2>", views.shapes_with_args),
3233
# *static_extensions(),
3334
# *staticfiles_urlpatterns(),
3435
]
@@ -44,5 +45,6 @@
4445
document("shape_viewer", views.shape_viewer_handler),
4546
autoload("sea-surface-temp", views.sea_surface_handler),
4647
autoload("sea_surface_custom_uri", views.sea_surface_handler),
47-
autoload('shapes', views.shape_viewer_handler),
48+
autoload("shapes", views.shape_viewer_handler),
49+
autoload("shapes/(?P<arg1>[\w_\-]+)/(?P<arg2>[\w_\-]+)", views.shape_viewer_handler_with_args),
4850
]

examples/django_embed/django_embed/views.py

+19-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from os.path import join
22
from typing import Any
3+
from bokeh_django import with_request, with_url_args
34

45
from django.conf import settings
56
from django.http import HttpRequest, HttpResponse
@@ -12,6 +13,7 @@
1213
from bokeh.plotting import figure
1314
from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature
1415
from bokeh.themes import Theme
16+
import panel as pn
1517

1618
from .shape_viewer import shape_viewer
1719

@@ -27,6 +29,15 @@ def shape_viewer_handler(doc: Document) -> None:
2729
panel.server_doc(doc)
2830

2931

32+
@with_url_args
33+
def shape_viewer_handler_with_args(doc, arg1, arg2):
34+
viewer = shape_viewer()
35+
pn.Column(
36+
viewer,
37+
pn.pane.Markdown(f'## This app has URL Args: {arg1} and {arg2}')
38+
).server_doc(doc)
39+
40+
3041
def sea_surface_handler(doc: Document) -> None:
3142
df = sea_surface_temperature.copy()
3243
source = ColumnDataSource(data=df)
@@ -49,12 +60,6 @@ def callback(attr: str, old: Any, new: Any) -> None:
4960
doc.add_root(column(slider, plot))
5061

5162

52-
def with_request(f):
53-
def wrapper(doc):
54-
return f(doc, doc.session_context.request)
55-
return wrapper
56-
57-
5863
@with_request
5964
def sea_surface_handler_with_template(doc: Document, request: Any) -> None:
6065
sea_surface_handler(doc)
@@ -76,15 +81,20 @@ def sea_surface_handler_with_template(doc: Document, request: Any) -> None:
7681

7782

7883
def sea_surface(request: HttpRequest) -> HttpResponse:
79-
script = server_document(request.build_absolute_uri())
84+
script = server_document(request.get_full_path())
8085
return render(request, "embed.html", dict(script=script))
8186

8287

8388
def sea_surface_custom_uri(request: HttpRequest) -> HttpResponse:
84-
script = server_document(request._current_scheme_host + "/sea_surface_custom_uri")
89+
script = server_document("/sea_surface_custom_uri")
8590
return render(request, "embed.html", dict(script=script))
8691

8792

8893
def shapes(request: HttpRequest) -> HttpResponse:
89-
script = server_document(request.build_absolute_uri())
94+
script = server_document(request.get_full_path())
95+
return render(request, "embed.html", dict(script=script))
96+
97+
98+
def shapes_with_args(request: HttpRequest, arg1: str, arg2: str) -> HttpResponse:
99+
script = server_document(request.get_full_path())
90100
return render(request, "embed.html", dict(script=script))

0 commit comments

Comments
 (0)