Skip to main content

File-system based router for Dash and Flash applications.

Project description

Dash Router

Introduction

DashRouter is a file-system based routing replacement for the default Dash pages module. Unlike the conventional direct mapping of pages, Dash Router resolves URLs segment-wise through a route tree. This approach allows you to break down your application into modular pieces, with each URL segment corresponding to a part of your layout and folder in your project. All routes are dynamic by default and have to be set static manually. Static routes utilize the native mapping with path template support like the dash pages module while dynamic routes are stored and executed via route tree. The tree is constructed by two route types:

  • nested routes: distinct segment inside the layout, URL and application
  • slots: silent route segment with isolated placement in the layout and folder

This segmentation not only makes your codebase easier to manage but also enhances performance through subrendering, parallel execution, and individualized error and loading handling.

routing Resolving a URL example

Note:

DashRouter supports both Dash and Flash, but the parallel execution paradimes only apply for Flash, due to its async capatilities. That means that the execution of slots and nested routes with Dash will always be in sequence but the subrenedering, error handling etc will be the same.

Conventions & Features

Note: Each level needs a distinct route into the next layer, that means that each level can only have one dynamic route. So a layer can have slots without any path and a nested route

  • Slots: Slots are defined by folder names starting and ending with (slot_name)
  • Nested Routes: Nested routes are defined by the foldername
  • Layouts: The router is looking for page.py files which must contain either a DashComponent or function named layout
  • Route Config: Routes can be configure with the RouteConfig object which has to be places in the corresponding page.py file
  • Path Templates: Path templates get set via the RouteConfig: pathtemplate='<customer_id>' and will be passed as keyword argument to the corresponding layout and all its child segments
  • Static Routes: Set is_static=True in the RouteConfig to render the layout as root without any children
  • Error: If the rendering of a layout failes, an individual error layout is rendered. To add a custome error handler, place an error.py next to your page.py file with either a DashComponent or function named layout
  • Loading: In upcoming version
  • App Modules: Folders without a page.py file can be removed from the url by setting ignore_empty_folders=True in the Router object, this can help to structure your application

Setup

Example app structure

.
├── app/
├── pages/                              -> Root Route
│   ├── sales/                          -> Root of Nested Route
│   │   ├── _components/
│   │   ├── invoices/                   -> Nested Route
│   │   │   ├── (invoice)/              -> Slot with path template
│   │   │   │   ├── items/
│   │   │   │   │   └── page.py
│   │   │   │   ├── positions/
│   │   │   │   │   └── page.py
│   │   │   │   └── page.py
│   │   │   ├── (vendors)/              -> Slot
│   │   │   │   └── page.py
│   │   │   └── page.py
│   │   ├── overview/
│   │   │   └── page.py
│   │   └── analytics/
│   │       └── page.py
│   ├── monitoring/                     -> Static Route
│   │   └── page.py
│   └── analytics/                      -> App Module
│       ├── dashboard_1/
│       │   └── page.py
│       ├── dashboard_2/
│       │   └── page.py
│       └── dashboard_3/
│           └── page.py
└── app.py

Create App

  • Instatiate the router - creates the execution tree (next version will be able to take a manually configured routing tree)
  • Insert the RootContainer - this where your content gets displayed
# app.py
from dash_router import Router, RootContainer
from flash import Flash
from dash import html


app = Flash(__name__, pages_folder='pages')
router = Router(app, ignore_empty_folders=True)

app.layout = html.Div([RootContainer()])
  • Don't set use_pages=True in Dash or Flash to avoid routing collisions

Create Basic Page

  • create a page.py file in your pages directory - this route is automatically treated as ' / '
  • define a layout (layout definitions for Dash must be sync)
# pages/page.py -> /
from dash import html
from dash_router import RouteConfig


config = RouteConfig(
    title='Home'
)

async def layout(**kwargs):
    return html.H1('Hello Flash')

Create Nested Routes

  • Routes and their folders are automatically nested routes
  • Nested routes get pass as ChildContainer to their parent layout
  • Parent layout defines the position of all child segments
  • ChildContainer have a special property props with the active attribute which contains the name of the active children
  • Set default child routes with default_child
  • upcoming version will have a method to securely create urls in your layout
# pages/sales/page.py -> /sales
from ._components.tabs import Tabs

from dash_router import ChildContainer
import dash_mantine_components as dmc


config = RouteConfig(default_child="overview")

async def layout(children: ChildContainer, **kwargs):
    return dmc.Stack(
        [
            dmc.Title("Sales", mt=0, pt=0),
            Tabs(active=children.props.active),
            dmc.Divider(),
            dmc.ScrollArea(
                children,
                h="calc(85vh)",
                type="auto",
                offsetScrollbars=True
            )
        ]
    )

Create Slots

  • Slots are silent route segments with the behaviour of a normal nested route
  • Positions also get managed by their parent layout

Parent Route:

# pages/sales/invoices/page.py -> /sales/invoices
from ._components.table import create_invoice_table
from ._api import get_data

from dash_router import SlotContainer
import dash_mantine_components as dmc


async def layout(vendors: SlotContainer, invoice: SlotContainer, **kwargs):

    data = await get_data()

    return dmc.SimpleGrid(
        cols=2,
        children=[
            dmc.Stack(
                [
                    dmc.Title("Top Vendors", order=2),
                    vendors,
                    dmc.Title("Invoice List", order=3),
                    create_invoice_table(data)
                ]
            ),
            dmc.Stack(
                [
                    dmc.Box(invoice, mih=50),
                    dmc.Alert(
                        "This is still the invoices section",
                        title="Invoices section!",
                    )
                ]
            )
        ]
    )
  • Slots can also be dynamic but either none, all or one
  • foldernames have to start and end with () to mark a route as slot
  • slots are independent fom each and therefor executed in parallel

Basic Slot:

# pages/sales/invoices/(vendors)/page.py -> /sales/invoices
from ._components.figures import create_barchart
from ._api import get_data


async def layout(**kwargs):
    data = await get_data()
    return create_barchart(data)

Path Variables

  • Path variables can be applied on static and slot routes
  • Path templates in dynamic routes have to be slots
  • Get defined in the RouteConfig and contain <>
  • static routes can have multiple kwargs in a pathtemplate slots only take one kwarg
  • upcoming version will have type conversions and a <...rest> template that takes all remaing segments as arguments

Slot with path variable:

# pages/sales/invoices/(invoice)/page.py -> /sales/invoices/<invoice_id>
from ._components.figures import create_comp_chart
from ._components.tabs import create_invoice_tabs
from ._api import get_data

from dash_router import RouteConfig, ChildContainer


config = RouteConfig(path_template="<invoice_id>", default_child="items")

async def layout(children: ChildNode, invoice_id: int = None, **kwargs):

    if not invoice_id:
        return dmc.Stack(
            [
                get_icon("carbon:select-01", height=60),
                dmc.Title("No invoice selected", order=3),
            ],
            align="center"
        )

    data = await get_data(invoice_id)

    return dmc.Stack(
        [
            dmc.Title("Invoice ID: " + str(invoice_id), order=2),
            dmc.Card(
                [
                    dmc.Title("All sales of vendor", order=3, mb="md"),
                    create_comp_chart(data),
                ]
            ),
            create_invoice_tabs(children.props.active, invoice_id),
            dmc.Card(children)
        ]
    )

Path template in static route:

# pages/monitoring/page.py -> /monitoring/<cid>
import dash_mantine_components as dmc
from router import RouteConfig


config = RouteConfig(path_template="<cid>", is_static=True)

async def layout(cid: str, **kwargs):
    return dmc.Title(f"Monitoring {cid}")
# pages/monitoring/page.py -> /monitoring/<cid>/unit/<other_id>
import dash_mantine_components as dmc
from router import RouteConfig


config = RouteConfig(path_template="<cid>/unit/<other_id>", is_static=True)

async def layout(cid: str, other_id: str, **kwargs):
    return dmc.Title(f"Monitoring {cid} {other_id}")

Error Layouts

  • Add error.py file to your route folder with DashComponent or function named layout for custom error handling
  • Layout receives the exception as well as the path variables and search params the layout used
  • A basic red card with the error message gets displayed by default
import dash_mantine_components as dmc


def layout(e: Exception, *args, **kwargs):
    return dmc.Alert(
        str(e),
        title="Error occured",
        color="red",
        variant="light"
    )

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flash_router-0.4.0b5.tar.gz (32.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flash_router-0.4.0b5-py3-none-any.whl (37.6 kB view details)

Uploaded Python 3

File details

Details for the file flash_router-0.4.0b5.tar.gz.

File metadata

  • Download URL: flash_router-0.4.0b5.tar.gz
  • Upload date:
  • Size: 32.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.2 CPython/3.12.7 Darwin/24.5.0

File hashes

Hashes for flash_router-0.4.0b5.tar.gz
Algorithm Hash digest
SHA256 1e64d9a1265e49359ca92ec268f7a20920e68e5f40596a7420e262a86fbe7ade
MD5 a5cad9e081b3c3b27ba1ac499fd2909e
BLAKE2b-256 8a6d7c4dfa0a2272d57fa516186a74e398648362960e5a9b9abdd32b4f23bde1

See more details on using hashes here.

File details

Details for the file flash_router-0.4.0b5-py3-none-any.whl.

File metadata

  • Download URL: flash_router-0.4.0b5-py3-none-any.whl
  • Upload date:
  • Size: 37.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.2 CPython/3.12.7 Darwin/24.5.0

File hashes

Hashes for flash_router-0.4.0b5-py3-none-any.whl
Algorithm Hash digest
SHA256 3aa474682e37f1ea0777f83f39e7149316f93938363409d38fafff7ac876ac73
MD5 8b06608e7a8f50f961b130a2573c07ad
BLAKE2b-256 053be4193f4bb23d84e38e0faa794c315fc0ae25b738407905c7af924efae32a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page