Django Apps

As the core user guides including the Introduction have demonstrated, it is easy to display Panel apps in the notebook, launch them from an interactive Python prompt, and deploy them as a standalone Bokeh server app from the commandline. However, it is also often useful to embed a Panel app in large web application, such as a Django web server. Using Panel with Django requires a bit more work than for notebooks and Bokeh servers.

To run this example app yourself, you will first need to install django 2 (e.g. conda install "django=2").

Sliders app

Based on a standard Django2 app template, this app shows how to integrate Panel with a Django view, but there's currently no interaction between the Param and Django models.

The sliders app is in examples/apps/django2/sliders. We will cover the following additions/modifications to the Django2 app template:

  • sliders/sinewave.py: a parameterized object (representing your pre-existing code)

  • sliders/bk_sliders.py: creates a Bokeh app function from the SineWave class

  • sliders/apps.py: how a Django app can import and use Bokeh server

  • sliders/views.py and templates/base.html: getting the Bokeh app into a Django view

You should be able to run this app yourself by changing to the examples/apps/django2 directory and then running: python manage.py runserver; then visit http://localhost:8000/sliders in your browser to try the app.

screenshot of sliders app

To start with, in sliders/sinewave.py we create a parameterized object to serve as a placeholder for your own, existing code:

In [1]:
import param
import numpy as np

from bokeh.models import ColumnDataSource
from bokeh.plotting import figure


class SineWave(param.Parameterized):

    offset = param.Number(default=0.0, bounds=(-5.0,5.0))
    amplitude = param.Number(default=1.0, bounds=(-5.0,5.0))
    phase = param.Number(default=0.0,bounds=(0.0,2*np.pi))
    frequency = param.Number(default=1.0, bounds=(0.1, 5.1))
    N = param.Integer(default=200, bounds=(0,None))
    x_range = param.Range(default=(0, 4*np.pi),bounds=(0,4*np.pi))
    y_range = param.Range(default=(-2.5,2.5),bounds=(-10,10))

    def __init__(self, **params):
        super(SineWave, self).__init__(**params)
        x, y = self.sine()
        self.cds = ColumnDataSource(data=dict(x=x, y=y))
        self.plot = figure(plot_height=400, plot_width=400,
                      tools="crosshair,pan,reset,save,wheel_zoom",
                           x_range=self.x_range, y_range=self.y_range)
        self.plot.line('x', 'y', source=self.cds, line_width=3, line_alpha=0.6)

    @param.depends('N', 'frequency', 'amplitude', 'offset', 'phase', 'x_range', 'y_range', watch=True)
    def update_plot(self):
        x, y = self.sine()
        self.cds.data = dict(x=x, y=y)
        self.plot.x_range.start, self.plot.x_range.end = self.x_range
        self.plot.y_range.start, self.plot.y_range.end = self.y_range

    def sine(self):
        x = np.linspace(0, 4*np.pi, self.N)
        y = self.amplitude*np.sin(self.frequency*x + self.phase) + self.offset
        return x, y

We then create an instance of the class, use it to declare a panel from the parameters and the Bokeh plot, and create a root model:

def app(doc):
    sw = SineWave()
    row = pn.Row(sw.param, sw.plot)
    row._get_root(doc)

The first Django-specific aspect of our example is to show how a Django app can import and use Bokeh server. This is based on https://github.com/bokeh/bokeh/blob/0.12.16/examples/howto/server_embed/flask_embed.py, which shows how to embed Bokeh in a Flask app.

from django.apps import AppConfig

from bokeh.server.server import Server

from tornado.ioloop import IOLoop

from . import bk_sliders
from . import bk_config

def bk_worker():
    # Note: num_procs must be 1; see e.g. flask_gunicorn_embed.py for num_procs>1
    server = Server({'/bk_sliders_app': bk_sliders.app},
                    io_loop=IOLoop(),
                    address=bk_config.server['address'],
                    port=bk_config.server['port'],
                    allow_websocket_origin=["localhost:8000"])
    server.start()
    server.io_loop.start()

class Sliders(AppConfig):
    name = 'sliders'
    def ready(self):
        from threading import Thread
        Thread(target=bk_worker).start()

Here, localhost:8000 is the address of the Django app. Note also we have made a simple config file, bk_config.py, for Bokeh server settings:

server = dict(
    address = "localhost",
    port = 5006
)

Finally, in sliders/views.py we create a view to display the Bokeh sliders app:

from django.shortcuts import render

from bokeh.embed import server_document

from . import bk_config

def sliders(request):
    return render(request, 'base.html', {
        "server_script": server_document('http://%s:%s/bk_sliders_app'%(bk_config.server['address'],
                                                                        bk_config.server['port']))})

The corresponding template is in templates/base.html:

{% block content %}
{{server_script|safe}}  
{% endblock %}

Right click to download this notebook from GitHub.