diff --git a/README.md b/README.md index 788708ee..77dd0e31 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,6 @@ -# Django IDOM - -
- -`django-idom` allows Django to integrate with [IDOM](https://github.com/idom-team/idom), -a package inspired by [ReactJS](https://reactjs.org/) for creating responsive web -interfaces in pure Python. +# Django IDOM · [](https://github.com/idom-team/django-idom/actions?query=workflow%3ATest) [](https://pypi.python.org/pypi/django-idom) [](https://github.com/idom-team/django-idom/blob/main/LICENSE) + +`django-idom` allows Django to integrate with [IDOM](https://github.com/idom-team/idom), a reactive Python web framework for building **interactive websites without needing a single line of Javascript**. **You can try IDOM now in a Jupyter Notebook:** -# Install Django IDOM +# Quick Example -```bash -pip install django-idom -``` +## `example_app/components.py` + +This is where you'll define your [IDOM](https://github.com/idom-team/idom) components. Ultimately though, you should +feel free to organize your component modules you wish. Any components created will ultimately be referenced +by Python dotted path in `your-template.html`. -# Django Integration +```python +from idom import component, html +from django_idom import IdomWebsocket -To integrate IDOM into your application you'll need to modify or add the following files to `your_project`: -``` -your_project/ -├── __init__.py -├── asgi.py -├── settings.py -├── urls.py -└── example_app/ - ├── __init__.py - ├── components.py - ├── templates/ - │ └── your-template.html - └── urls.py +@component +def Hello(websocket: IdomWebsocket, greeting_recipient: str): # Names are CamelCase by ReactJS convention + return html.header(f"Hello {greeting_recipient}!") ``` -## `asgi.py` +## [`example_app/templates/your-template.html`](https://docs.djangoproject.com/en/dev/topics/templates/) -Follow the [`channels`](https://channels.readthedocs.io/en/stable/) -[installation guide](https://channels.readthedocs.io/en/stable/installation.html) in -order to create ASGI websockets within Django. Then, we will add a path for IDOM's -websocket consumer using `IDOM_WEBSOCKET_PATH`. +In your templates, you may add IDOM components into your HTML by using the `idom_component` +template tag. This tag requires the dotted path to the component function. Additonally, you can +pass in keyworded arguments into your component function. -_Note: If you wish to change the route where this websocket is served from, see the -available [settings](#settingspy)._ +In context this will look a bit like the following... -```python +```jinja +{% load idom %} -import os + + + + ... + {% idom_component "my_django_project.example_app.components.Hello" greeting_recipient="World" %} + + +``` -from django.core.asgi import get_asgi_application +# Installation -from django_idom import IDOM_WEBSOCKET_PATH +Install `django-idom` via pip. -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_app.settings") +```bash +pip install django-idom +``` -# Fetch ASGI application before importing dependencies that require ORM models. -http_asgi_app = get_asgi_application() +--- -from channels.auth import AuthMiddlewareStack -from channels.routing import ProtocolTypeRouter, URLRouter +You'll also need to modify a few files in your Django project... -application = ProtocolTypeRouter( - { - "http": http_asgi_app, - "websocket": SessionMiddlewareStack( - AuthMiddlewareStack(URLRouter([IDOM_WEBSOCKET_PATH])) - ), - } -) -``` - -## `settings.py` +## [`settings.py`](https://docs.djangoproject.com/en/dev/topics/settings/) In your settings you'll need to add `django_idom` to the -[`INSTALLED_APPS`](https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-INSTALLED_APPS) +[`INSTALLED_APPS`](https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-INSTALLED_APPS) list: ```python @@ -100,104 +76,63 @@ INSTALLED_APPS = [ ] ``` -You may configure additional options as well: +You may configure additional options as well... ```python -# the base URL for all IDOM-releated resources -IDOM_BASE_URL: str = "_idom/" +# If "idom" cache is not configured, then we'll use the "default" instead +CACHES = { + "idom": {"BACKEND": ...}, +} # Maximum seconds between two reconnection attempts that would cause the client give up. # 0 will disable reconnection. IDOM_WS_MAX_RECONNECT_DELAY: int = 604800 -# Configure a cache for loading JS files -CACHES = { - # If "idom" cache is not configured, then we'll use the "default" instead - "idom": {"BACKEND": ...}, -} +# The URL for IDOM to serve its Websockets +IDOM_WEBSOCKET_URL: str = "idom/" ``` -## `urls.py` +## [`urls.py`](https://docs.djangoproject.com/en/dev/topics/http/urls/) -You'll need to include IDOM's static web modules path using `IDOM_WEB_MODULES_PATH`. -Similarly to the `IDOM_WEBSOCKET_PATH`. If you wish to change the route where this -websocket is served from, see the available [settings](#settings.py). +Add Django-IDOM http URLs to your `urlpatterns`. ```python -from django_idom import IDOM_WEB_MODULES_PATH - urlpatterns = [ - IDOM_WEB_MODULES_PATH, + path("idom/", include("django_idom.http.urls")), ... ] ``` -## `example_app/components.py` - -This is where, by a convention similar to that of -[`views.py`](https://docs.djangoproject.com/en/3.2/topics/http/views/), you'll define -your [IDOM](https://github.com/idom-team/idom) components. Ultimately though, you should -feel free to organize your component modules you wish. The components created here will -ultimately be referenced by name in `your-template.html`. `your-template.html`. - -```python -import idom - -@idom.component -def Hello(websocket, greeting_recipient): # component names are camelcase by convention - return idom.html.header(f"Hello {greeting_recipient}!") -``` - -## `example_app/templates/your-template.html` - -In your templates, you may inject a view of an IDOM component into your templated HTML -by using the `idom_component` template tag. This tag which requires the name of a component -to render (of the form `module_name.ComponentName`) and keyword arguments you'd like to -pass it from the template. - -```python -idom_component module_name.ComponentName param_1="something" param_2="something-else" -``` - -In context this will look a bit like the following... - -```jinja -{% load idom %} +## [`asgi.py`](https://docs.djangoproject.com/en/dev/howto/deployment/asgi/) - - - - ... - {% idom_component "your_project.example_app.components.Hello" greeting_recipient="World" %} - - -``` +If you do not have an `asgi.py`, first follow the [`channels` installation guide](https://channels.readthedocs.io/en/stable/installation.html) in +order to create websockets within Django. -## `example_app/views.py` +We will add IDOM's websocket consumer path using `IDOM_WEBSOCKET_PATH`. -You can then serve `your-template.html` from a view just -[like any other](https://docs.djangoproject.com/en/3.2/intro/tutorial03/#write-views-that-actually-do-something). +_Note: If you wish to change the route where this websocket is served from, see the +available [settings](#settingspy)._ ```python -from django.shortcuts import render - -def your_view(request): - context = {} - return render(request, "your-template.html", context) -``` -## `example_app/urls.py` +import os +from django.core.asgi import get_asgi_application +from django_idom import IDOM_WEBSOCKET_PATH -Include your view in the list of urlpatterns +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_app.settings") +http_asgi_app = get_asgi_application() -```python -from django.urls import path -from .views import your_view # define this view like any other HTML template view +from channels.auth import AuthMiddlewareStack +from channels.routing import ProtocolTypeRouter, URLRouter -urlpatterns = [ - path("", your_view), - ... -] +application = ProtocolTypeRouter( + { + "http": http_asgi_app, + "websocket": SessionMiddlewareStack( + AuthMiddlewareStack(URLRouter([IDOM_WEBSOCKET_PATH])) + ), + } +) ``` # Developer Guide diff --git a/src/django_idom/__init__.py b/src/django_idom/__init__.py index 90a4aba1..f94066ea 100644 --- a/src/django_idom/__init__.py +++ b/src/django_idom/__init__.py @@ -1,5 +1,6 @@ -from .paths import IDOM_WEB_MODULES_PATH, IDOM_WEBSOCKET_PATH +from .websocket.consumer import IdomWebsocket +from .websocket.paths import IDOM_WEBSOCKET_PATH __version__ = "0.0.1" -__all__ = ["IDOM_WEB_MODULES_PATH", "IDOM_WEBSOCKET_PATH"] +__all__ = ["IDOM_WEBSOCKET_PATH", "IdomWebsocket"] diff --git a/src/django_idom/config.py b/src/django_idom/config.py index 0664c964..37d78ad0 100644 --- a/src/django_idom/config.py +++ b/src/django_idom/config.py @@ -7,11 +7,10 @@ IDOM_REGISTERED_COMPONENTS: Dict[str, ComponentConstructor] = {} -IDOM_BASE_URL = getattr(settings, "IDOM_BASE_URL", "_idom/") -IDOM_WEBSOCKET_URL = IDOM_BASE_URL + "websocket/" -IDOM_WEB_MODULES_URL = IDOM_BASE_URL + "web_module/" +IDOM_WEBSOCKET_URL = getattr(settings, "IDOM_WEBSOCKET_URL", "idom/") IDOM_WS_MAX_RECONNECT_DELAY = getattr(settings, "IDOM_WS_MAX_RECONNECT_DELAY", 604800) +# Determine if using Django caching or LRU cache if "idom" in getattr(settings, "CACHES", {}): IDOM_CACHE = caches["idom"] else: diff --git a/src/django_idom/http/__init__.py b/src/django_idom/http/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/django_idom/http/urls.py b/src/django_idom/http/urls.py new file mode 100644 index 00000000..019a603e --- /dev/null +++ b/src/django_idom/http/urls.py @@ -0,0 +1,14 @@ +from django.urls import path + +from . import views + + +app_name = "idom" + +urlpatterns = [ + path( + "web_module/