-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Single Page Application (SPA) compatibility via
reactpy-router
(#185)
Port reactpy-router into a Django equivalent (using Django's URL matching schema)
- Loading branch information
1 parent
6fb7ba2
commit 023cb15
Showing
24 changed files
with
293 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from reactpy import component, html | ||
from reactpy_django.router import django_router | ||
from reactpy_router import route | ||
|
||
|
||
@component | ||
def my_component(): | ||
return django_router( | ||
route("/router/", html.div("Example 1")), | ||
route("/router/any/<value>/", html.div("Example 2")), | ||
route("/router/integer/<int:value>/", html.div("Example 3")), | ||
route("/router/path/<path:value>/", html.div("Example 4")), | ||
route("/router/slug/<slug:value>/", html.div("Example 5")), | ||
route("/router/string/<str:value>/", html.div("Example 6")), | ||
route("/router/uuid/<uuid:value>/", html.div("Example 7")), | ||
route("/router/two_values/<int:value>/<str:value2>/", html.div("Example 9")), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
## Overview | ||
|
||
<p class="intro" markdown> | ||
|
||
A variant of [`reactpy-router`](https://github.com/reactive-python/reactpy-router) that utilizes Django conventions. | ||
|
||
</p> | ||
|
||
!!! abstract "Note" | ||
|
||
Looking for more details on URL routing? | ||
|
||
This package only contains Django specific URL routing features. Standard features can be found within [`reactive-python/reactpy-router`](https://reactive-python.github.io/reactpy-router/). | ||
|
||
--- | ||
|
||
## `#!python django_router(*routes)` | ||
|
||
=== "components.py" | ||
|
||
```python | ||
{% include "../../python/django-router.py" %} | ||
``` | ||
|
||
??? example "See Interface" | ||
|
||
<font size="4">**Parameters**</font> | ||
|
||
| Name | Type | Description | Default | | ||
| --- | --- | --- | --- | | ||
| `#!python *routes` | `#!python Route` | An object from `reactpy-router` containing a `#!python path`, `#!python element`, and child `#!python *routes`. | N/A | | ||
|
||
<font size="4">**Returns**</font> | ||
|
||
| Type | Description | | ||
| --- | --- | | ||
| `#!python VdomDict | None` | The matched component/path after it has been fully rendered. | | ||
|
||
??? question "How is this different from `#!python reactpy_router.simple.router`?" | ||
|
||
This component utilizes `reactpy-router` under the hood, but provides a more Django-like URL routing syntax. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,17 @@ | ||
export type ReconnectOptions = { | ||
startInterval: number; | ||
maxInterval: number; | ||
maxRetries: number; | ||
backoffMultiplier: number; | ||
} | ||
startInterval: number; | ||
maxInterval: number; | ||
maxRetries: number; | ||
backoffMultiplier: number; | ||
}; | ||
|
||
export type ReactPyUrls = { | ||
componentUrl: string; | ||
query: string; | ||
jsModules: string; | ||
} | ||
componentUrl: URL; | ||
query: string; | ||
jsModules: string; | ||
}; | ||
|
||
export type ReactPyDjangoClientProps = { | ||
urls: ReactPyUrls; | ||
reconnectOptions: ReconnectOptions; | ||
} | ||
urls: ReactPyUrls; | ||
reconnectOptions: ReconnectOptions; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from reactpy_django.router.components import django_router | ||
|
||
__all__ = ["django_router"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
from __future__ import annotations | ||
|
||
import re | ||
from typing import Any | ||
|
||
from reactpy_router.core import create_router | ||
from reactpy_router.simple import ConverterMapping | ||
from reactpy_router.types import Route | ||
|
||
from reactpy_django.router.converters import CONVERTERS | ||
|
||
PARAM_PATTERN = re.compile(r"<(?P<type>\w+:)?(?P<name>\w+)>") | ||
|
||
|
||
# TODO: Make reactpy_router's SimpleResolver generic enough to where we don't have to define our own | ||
class DjangoResolver: | ||
"""A simple route resolver that uses regex to match paths""" | ||
|
||
def __init__(self, route: Route) -> None: | ||
self.element = route.element | ||
self.pattern, self.converters = parse_path(route.path) | ||
self.key = self.pattern.pattern | ||
|
||
def resolve(self, path: str) -> tuple[Any, dict[str, Any]] | None: | ||
match = self.pattern.match(path) | ||
if match: | ||
return ( | ||
self.element, | ||
{k: self.converters[k](v) for k, v in match.groupdict().items()}, | ||
) | ||
return None | ||
|
||
|
||
# TODO: Make reactpy_router's parse_path generic enough to where we don't have to define our own | ||
def parse_path(path: str) -> tuple[re.Pattern[str], ConverterMapping]: | ||
pattern = "^" | ||
last_match_end = 0 | ||
converters: ConverterMapping = {} | ||
for match in PARAM_PATTERN.finditer(path): | ||
param_name = match.group("name") | ||
param_type = (match.group("type") or "str").strip(":") | ||
try: | ||
param_conv = CONVERTERS[param_type] | ||
except KeyError as e: | ||
raise ValueError( | ||
f"Unknown conversion type {param_type!r} in {path!r}" | ||
) from e | ||
pattern += re.escape(path[last_match_end : match.start()]) | ||
pattern += f"(?P<{param_name}>{param_conv['regex']})" | ||
converters[param_name] = param_conv["func"] | ||
last_match_end = match.end() | ||
pattern += f"{re.escape(path[last_match_end:])}$" | ||
return re.compile(pattern), converters | ||
|
||
|
||
django_router = create_router(DjangoResolver) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from django.urls.converters import get_converters | ||
from reactpy_router.simple import ConversionInfo | ||
|
||
CONVERTERS: dict[str, ConversionInfo] = { | ||
name: {"regex": converter.regex, "func": converter.to_python} | ||
for name, converter in get_converters().items() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
from reactpy import component, html, use_location | ||
from reactpy_django.router import django_router | ||
from reactpy_router import route, use_params, use_query | ||
|
||
|
||
@component | ||
def display_params(*args): | ||
params = use_params() | ||
return html._( | ||
html.div(f"Params: {params}"), | ||
*args, | ||
) | ||
|
||
|
||
@component | ||
def main(): | ||
location = use_location() | ||
query = use_query() | ||
|
||
route_info = html._( | ||
html.div( | ||
{"id": "router-path", "data-path": location.pathname}, | ||
f"Path Name: {location.pathname}", | ||
), | ||
html.div(f"Query String: {location.search}"), | ||
html.div(f"Query: {query}"), | ||
) | ||
|
||
return django_router( | ||
route("/router/", html.div("Path 1", route_info)), | ||
route("/router/any/<value>/", display_params("Path 2", route_info)), | ||
route("/router/integer/<int:value>/", display_params("Path 3", route_info)), | ||
route("/router/path/<path:value>/", display_params("Path 4", route_info)), | ||
route("/router/slug/<slug:value>/", display_params("Path 5", route_info)), | ||
route("/router/string/<str:value>/", display_params("Path 6", route_info)), | ||
route("/router/uuid/<uuid:value>/", display_params("Path 7", route_info)), | ||
route("/router/", None, route("abc/", display_params("Path 8", route_info))), | ||
route( | ||
"/router/two/<int:value>/<str:value2>/", | ||
display_params("Path 9", route_info), | ||
), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from django.urls import re_path | ||
|
||
from test_app.router.views import router | ||
|
||
urlpatterns = [ | ||
re_path(r"^router/(?P<path>.*)/?$", router), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from django.shortcuts import render | ||
|
||
|
||
def router(request, path=None): | ||
return render(request, "router.html", {}) |
Oops, something went wrong.