django-plotly-dash

repository·master·Indexed 20 days ago

https://github.com/gibbsconsulting/django-plotly-dash

A library that allows developers to expose Plotly Dash applications as Django tags. It enables embedding multiple Dash apps into a single web page, sharing internal state between them, and providing Dash apps with access to Django's user and session context. The package includes support for custom access control via view decorators, integration with dash-bootstrap-components, and configurable websocket and HTTP message endpoints.

Tokens
13.8K
Snippets
46
Records
60
Agent score
64%

What's inside django-plotly-dash

  1. What is django-plotly-dash

    master

    django-plotly-dash is a package that enables Plotly Dash applications to be served within a Django application. It allows you to integrate Dash features into the Django ecosystem, providing the following capabilities:

    • Multi-app support: Multiple Dash applications can coexist on a single page.
    • State Persistence: Separate instances of a Dash application can persist their internal state using Django models.
    • Django Integration: Leverage existing Django user management, access control, and infrastructure.
    • Unified Scaling: Consolidate Dash and Django into a single server process to simplify deployment and scaling.
  2. Manage application state with the DashApp model

    master

    The DashApp model allows you to persist a subset of a Dash application's internal state as serialized JSON in the base_state field.

    State Lifecycle and Synchronization

    • Initialization: Use populate_values() to insert all possible initial values from the Dash layout configuration into base_state.
    • Retrieval: When a Dash application requests its state (via the <app slug>_dash-layout URL), any values present in base_state overwrite the values in the underlying application.
    • Updating State: You can update tracked variables using update_current_state(wid, key, value). This method is automatically called for any callback argument and return value.
    • Persistence: If the save_on_change boolean flag is set to True, the model instance will automatically call its save() method after a callback finishes, provided changes were detected via update_current_state.
    # Example of manual state update within a callback context
    dash_app_instance.update_current_state(wid='my-component-id', key='value_key', value='new_value')
    
    # If save_on_change is True, the change is persisted to the database after the callback
  3. Use session_state for user-specific data persistence

    master

    The session_state parameter provides a dictionary unique to the current user session. Any modifications made to this dictionary within a callback are automatically persisted using the Django session framework.

    Important Note on Propagation: Changes to session_state (or other server-side objects) are not automatically pushed to the front-end UI. The latest version of these objects will only be available to a callback when it is triggered by a UI event (like an Input or State change). To enable direct updating of applications without a UI trigger, you must use an explicit pipe as described in the django-plotly-dash live updating documentation.

  4. How websocket and HTTP message endpoints work

    master

    The websocket and direct HTTP message endpoints are separately configurable via ws_route and http_route. This separation serves two purposes:

    1. ASGI/WSGI Isolation: It allows asynchronous routes (like websocket connections) to be served via an ASGI server (e.g., daphne), while synchronous routes are served via a WSGI server (e.g., gunicorn). A reverse proxy like nginx can route traffic to the appropriate server based on the URL.
    2. Security/Privacy: The HTTP endpoint can be used as a private service to allow other parts of the application to send notifications to Dash apps without exposing this functionality as part of the public API.
  5. How django-plotly-dash works

    master

    The package works by wrapping the standard dash.Dash object. It maps the HTTP endpoints exposed by the Dash application to Django endpoints.

    To display a Dash application within a Django webpage, you use a specific template tag.

    Key Architectural Concepts:

    • Embedding: Dash applications are embedded into Django templates via template tags.
    • Stateful Applications: You can persist a subset of a Dash application's internal state as a standard Django model instance. This allows the application to be available at its own unique URL and can be embedded into multiple pages.
    • Enhanced Callbacks: The package provides an enhanced version of the standard Dash callback. This version grants callbacks access to:
      • The current Django User.
      • The current Django session.
      • The Django model instance associated with the application's internal state (if using stateful applications).
  6. How live updating works with Pipe components

    master

    Live updating in django-plotly-dash uses Dash components and Django Channels to provide websocket endpoints for server-initiated messages.

    The Workflow

    1. Server-initiated message: The server sends a message to a named channel.
    2. Client injection: The message is sent to all interested clients via websockets.
    3. Callback invocation: The message content is injected into the client-side application and handled like any other value passed to a Dash callback function.

    Key Constraints

    • JSON Serialisable: Messages must be JSON serialisable because they travel from server $\rightarrow$ client $\rightarrow$ server.
    • Message Size: Keep messages small to minimize latency and bandwidth.
    • Round-trip Design: The design forces a round-trip (server to client and back to server) so that data is treated as client-side state, preventing the server from needing to maintain a separate copy of the application state.
    • Reliability: Message delivery is 'hopefully at least once'. Applications should be robust against both message loss and duplicate deliveries. It is recommended to use idempotent message patterns (e.g., "check if X needs to be done" rather than "do X").
    # Concept: Server -> Pipe Component -> Dash Callback
    # The Pipe component listens for messages on a channel and makes them available to callbacks.
  7. Understand the difference between StatelessApp and DashApp models

    master

    The django_plotly_dash application uses two primary models to manage Dash applications and their state:

    1. StatelessApp: Represents the definition of a single Dash application. Every time a DjangoDash object is instantiated, a corresponding StatelessApp is registered. It acts as a registry and provides access to the DjangoDash object via the as_dash_app() method.

    2. DashApp: Represents a specific instance of an application with a particular state. While a StatelessApp defines what the app is, a DashApp tracks how a specific user or session is interacting with it (its internal state). A DashApp instance is linked to a StatelessApp via a foreign key and is uniquely identified in URLs by its slug.

    In short: StatelessApp is the template/definition, and DashApp is the stateful instance.

  8. Configure local Dash component serving

    master
    During development, you can serve Dash components locally. While you can pass serve_locally=True to a DjangoDash constructor to serve CSS and JS files from the local server, it is recommended to use the global serve_locally configuration setting instead. Note: Do not serve static content through Django in production.
  9. Use dpd-static-support to serve component assets locally

    master

    To avoid dependencies on external URLs for components like dash-bootstrap-components, you can use the dpd-static-support package. This package provides mappings to locally served versions of common external assets.

    To implement this, you must:

    1. Install the package via pip.
    2. Add 'dpd_static_support' to your INSTALLED_APPS.
    3. Add 'dpd_static_support' to your PLOTLY_COMPONENTS list in settings.py.
    4. Ensure 'django_plotly_dash.middleware.ExternalRedirectionMiddleware' is included in your MIDDLEWARE configuration.
    pip install dpd-static-support
    INSTALLED_APPS = [
        ...
        'dpd_static_support',
    ]
    
    MIDDLEWARE = [
        ...
        'django_plotly_dash.middleware.ExternalRedirectionMiddleware',
    ]
    
    PLOTLY_COMPONENTS = [
        ...
        'dpd_static_support'
    ]
  10. Configure plotly_header and plotly_footer for direct injection

    master

    When using {% plotly_direct %}, you must include these two tags to ensure the app's CSS and JS are loaded correctly. These tags can be safely included in a base template with minimal overhead.

    Example Implementation:

    <!-- templates/base.html -->
    <!DOCTYPE html>
    <html>
        <head>
            ...
            {% load plotly_dash %}
            {% plotly_header %}
            ...
        </head>
        <body>
            ...
            {% plotly_direct name="SimpleExample" %}
            ...
            {% plotly_footer %}
        </body>
    </html>

    Requirement: Ensure 'django_plotly_dash.middleware.BaseMiddleware' is in your MIDDLEWARE settings.

    # settings.py
    MIDDLEWARE = [
        ...
        'django_plotly_dash.middleware.BaseMiddleware',
    ]