diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 95ad9c16..f237e91d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,3 +1,5 @@ +*By submitting this pull request you agree that all contributions to this project are made under the MIT license.* + ## Description A summary of the changes. diff --git a/CHANGELOG.md b/CHANGELOG.md index 55a8c0f5..dcda688d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,18 @@ Using the following categories, list your changes in this order: - Nothing (yet) +## [3.2.0] - 2023-06-08 + +### Added + +- Added warning if poor system/cache/database performance is detected while in `DEBUG` mode. +- Added `REACTPY_AUTH_BACKEND` setting to allow for custom authentication backends. + +### Changed + +- Using `SessionMiddlewareStack` is now optional. +- Using `AuthMiddlewareStack` is now optional. + ## [3.1.0] - 2023-05-06 ### Added @@ -203,7 +215,7 @@ Using the following categories, list your changes in this order: ### Added -- Django-specific hooks! `use_websocket`, `use_scope`, and `use_location` are now available within the `django_idom.hooks` module. +- Django specific hooks! `use_websocket`, `use_scope`, and `use_location` are now available within the `django_idom.hooks` module. - Documentation has been placed into a formal docs webpage. - Logging for when a component fails to import, or if no components were found within Django. @@ -277,7 +289,8 @@ Using the following categories, list your changes in this order: - Support for IDOM within the Django -[unreleased]: https://github.com/reactive-python/reactpy-django/compare/3.1.0...HEAD +[unreleased]: https://github.com/reactive-python/reactpy-django/compare/3.2.0...HEAD +[3.2.0]: https://github.com/reactive-python/reactpy-django/compare/3.1.0...3.2.0 [3.1.0]: https://github.com/reactive-python/reactpy-django/compare/3.0.1...3.1.0 [3.0.1]: https://github.com/reactive-python/reactpy-django/compare/3.0.0-reactpy...3.0.1 [3.0.0-reactpy]: https://github.com/reactive-python/reactpy-django/compare/3.0.0...3.0.0-reactpy diff --git a/README.md b/README.md index 90ec4754..f2d0f592 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,23 @@ -[![Tests](https://github.com/reactive-python/reactpy-django/workflows/Test/badge.svg?event=push)](https://github.com/reactive-python/reactpy-django/actions?query=workflow%3ATest) [![PyPI Version](https://img.shields.io/pypi/v/reactpy-django.svg?label=PyPI)](https://pypi.python.org/pypi/reactpy-django) [![License](https://img.shields.io/badge/License-MIT-purple.svg)](https://github.com/reactive-python/reactpy-django/blob/main/LICENSE) [![Docs](https://img.shields.io/website?down_message=offline&label=Docs&logo=read%20the%20docs&logoColor=white&up_message=online&url=https%3A%2F%2Freactive-python.github.io%2Freactpy-django%2F)](https://reactive-python.github.io/reactpy-django/) +

+ + + + + + + + + + + + + + + +

@@ -86,10 +102,11 @@ Additionally, you can pass in `args` and `kwargs` into your component function. Follow the links below to find out more about this project. -- [Try it Now](https://mybinder.org/v2/gh/reactive-python/reactpy-jupyter/main?urlpath=lab/tree/notebooks/introduction.ipynb) - check out ReactPy in a Jupyter Notebook. -- [Documentation](https://reactive-python.github.io/reactpy-django) - learn how to install, run, and use ReactPy. -- [Community Forum](https://github.com/reactive-python/reactpy/discussions) - ask questions, share ideas, and show off projects. -- [Contributor Guide](https://reactive-python.github.io/reactpy-django/contribute/code/) - see how you can help develop this project. -- [Code of Conduct](https://github.com/reactive-python/reactpy-django/blob/main/CODE_OF_CONDUCT.md) - standards for interacting with this community. +- [Try ReactPy (Jupyter Notebook)](https://mybinder.org/v2/gh/reactive-python/reactpy-jupyter/main?urlpath=lab/tree/notebooks/introduction.ipynb) +- [Documentation](https://reactive-python.github.io/reactpy-django) +- [GitHub Discussions](https://github.com/reactive-python/reactpy-django/discussions) +- [Discord](https://discord.gg/uNb5P4hA9X) +- [Contributor Guide](https://reactive-python.github.io/reactpy-django/contribute/code/) +- [Code of Conduct](https://github.com/reactive-python/reactpy-django/blob/main/CODE_OF_CONDUCT.md) diff --git a/docs/python/configure-asgi-middleware.py b/docs/python/configure-asgi-middleware.py new file mode 100644 index 00000000..0ee33a8a --- /dev/null +++ b/docs/python/configure-asgi-middleware.py @@ -0,0 +1,26 @@ +# Broken load order, only used for linting +from channels.routing import ProtocolTypeRouter, URLRouter + +from reactpy_django import REACTPY_WEBSOCKET_PATH + + +django_asgi_app = "" + + +# start +from channels.auth import AuthMiddlewareStack # noqa: E402 +from channels.sessions import SessionMiddlewareStack # noqa: E402 + + +application = ProtocolTypeRouter( + { + "http": django_asgi_app, + "websocket": SessionMiddlewareStack( + AuthMiddlewareStack( + URLRouter( + [REACTPY_WEBSOCKET_PATH], + ) + ) + ), + } +) diff --git a/docs/python/configure-asgi.py b/docs/python/configure-asgi.py index 88b37b1e..6ae7cf8a 100644 --- a/docs/python/configure-asgi.py +++ b/docs/python/configure-asgi.py @@ -10,9 +10,7 @@ django_asgi_app = get_asgi_application() -from channels.auth import AuthMiddlewareStack # noqa: E402 from channels.routing import ProtocolTypeRouter, URLRouter # noqa: E402 -from channels.sessions import SessionMiddlewareStack # noqa: E402 from reactpy_django import REACTPY_WEBSOCKET_PATH # noqa: E402 @@ -20,8 +18,6 @@ application = ProtocolTypeRouter( { "http": django_asgi_app, - "websocket": SessionMiddlewareStack( - AuthMiddlewareStack(URLRouter([REACTPY_WEBSOCKET_PATH])) - ), + "websocket": URLRouter([REACTPY_WEBSOCKET_PATH]), } ) diff --git a/docs/python/settings.py b/docs/python/settings.py index c3c2b0d9..9633da43 100644 --- a/docs/python/settings.py +++ b/docs/python/settings.py @@ -13,3 +13,10 @@ # Dotted path to the default `reactpy_django.hooks.use_query` postprocessor function, or `None` REACTPY_DEFAULT_QUERY_POSTPROCESSOR = "reactpy_django.utils.django_query_postprocessor" + +# Dotted path to the Django authentication backend to use for ReactPy components +# This is only needed if: +# 1. You are using `AuthMiddlewareStack` and... +# 2. You are using Django's `AUTHENTICATION_BACKENDS` settings and... +# 3. Your Django user model does not define a `backend` attribute +REACTPY_AUTH_BACKEND = None diff --git a/docs/src/changelog/index.md b/docs/src/changelog/index.md index 20120d5e..9c2bef97 100644 --- a/docs/src/changelog/index.md +++ b/docs/src/changelog/index.md @@ -7,6 +7,4 @@ hide: {% include-markdown "../../../CHANGELOG.md" start="" end="" %} ---- - {% include-markdown "../../../CHANGELOG.md" start="" %} diff --git a/docs/src/contribute/code.md b/docs/src/contribute/code.md index 05cf0ca6..07824927 100644 --- a/docs/src/contribute/code.md +++ b/docs/src/contribute/code.md @@ -1,6 +1,6 @@ ## Overview -!!! summary +!!! summary "Overview" You will need to set up a Python environment to develop ReactPy-Django. @@ -8,8 +8,6 @@ Everything within the `reactpy-django` repository must be specific to Django integration. Check out the [ReactPy Core documentation](https://reactpy.dev/docs/about/contributor-guide.html) to contribute general features such as: components, hooks, events, and more. ---- - ## Modifying Code If you plan to make code changes to this repository, you will need to install the following dependencies first: diff --git a/docs/src/contribute/docs.md b/docs/src/contribute/docs.md index 549ac780..d5a02be7 100644 --- a/docs/src/contribute/docs.md +++ b/docs/src/contribute/docs.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" You will need to set up a Python environment to preview docs changes. ---- - ## Modifying Docs If you plan to make changes to this documentation, you will need to install the following dependencies first: diff --git a/docs/src/contribute/running-tests.md b/docs/src/contribute/running-tests.md index 6034253f..b714c5cd 100644 --- a/docs/src/contribute/running-tests.md +++ b/docs/src/contribute/running-tests.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" You will need to set up a Python environment to run out test suite. ---- - ## Running Tests This repository uses [Nox](https://nox.thea.codes/en/stable/) to run tests. For a full test of available scripts run `nox -l`. diff --git a/docs/src/features/components.md b/docs/src/features/components.md index bf33af2e..d7926803 100644 --- a/docs/src/features/components.md +++ b/docs/src/features/components.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" Prefabricated components can be used within your `components.py` to help simplify development. ---- - ## View To Component Convert any Django view into a ReactPy component by using this decorator. Compatible with [Function Based Views](https://docs.djangoproject.com/en/dev/topics/http/views/) and [Class Based Views](https://docs.djangoproject.com/en/dev/topics/class-based-views/). Views can be sync or async. diff --git a/docs/src/features/decorators.md b/docs/src/features/decorators.md index ed2b8d09..45548799 100644 --- a/docs/src/features/decorators.md +++ b/docs/src/features/decorators.md @@ -1,16 +1,14 @@ ## Overview -!!! summary +!!! summary "Overview" Decorator utilities can be used within your `components.py` to help simplify development. ---- - ## Auth Required You can limit access to a component to users with a specific `auth_attribute` by using this decorator (with or without parentheses). -By default, this decorator checks if the user is logged in, and his/her account has not been deactivated. +By default, this decorator checks if the user is logged in and not deactivated (`is_active`). This decorator is commonly used to selectively render a component only if a user [`is_staff`](https://docs.djangoproject.com/en/dev/ref/contrib/auth/#django.contrib.auth.models.User.is_staff) or [`is_superuser`](https://docs.djangoproject.com/en/dev/ref/contrib/auth/#django.contrib.auth.models.User.is_superuser). diff --git a/docs/src/features/hooks.md b/docs/src/features/hooks.md index dd53ac61..5916776a 100644 --- a/docs/src/features/hooks.md +++ b/docs/src/features/hooks.md @@ -1,17 +1,15 @@ ## Overview -!!! summary +!!! summary "Overview" Prefabricated hooks can be used within your `components.py` to help simplify development. ??? tip "Looking for standard React hooks?" - Standard hooks are contained within [`reactive-python/reactpy`](https://github.com/reactive-python/reactpy). Since `reactpy` is installed alongside `reactpy-django`, you can import them at any time. + The `reactpy-django` package only contains django specific hooks. Standard hooks can be found within [`reactive-python/reactpy`](https://github.com/reactive-python/reactpy). Since `reactpy` is installed alongside `reactpy-django`, you can import them at any time. Check out the [ReactPy Core docs](https://reactpy.dev/docs/reference/hooks-api.html#basic-hooks) to see what hooks are available! ---- - ## Use Query The `use_query` hook is used fetch Django ORM queries. diff --git a/docs/src/features/settings.md b/docs/src/features/settings.md index 1454f6aa..3abd6575 100644 --- a/docs/src/features/settings.md +++ b/docs/src/features/settings.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" Your **Django project's** `settings.py` can modify the behavior of ReactPy. ---- - ## Primary Configuration These are ReactPy-Django's default settings values. You can modify these values in your **Django project's** `settings.py` to change the behavior of ReactPy. diff --git a/docs/src/features/template-tag.md b/docs/src/features/template-tag.md index 266b75ec..9d4ca18c 100644 --- a/docs/src/features/template-tag.md +++ b/docs/src/features/template-tag.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" Template tags can be used within your Django templates such as `my-template.html` to import ReactPy features. ---- - ## Component The `component` template tag can be used to insert any number of ReactPy components onto your page. diff --git a/docs/src/features/utils.md b/docs/src/features/utils.md index 73c5248d..9cec1aa4 100644 --- a/docs/src/features/utils.md +++ b/docs/src/features/utils.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" Utility functions that you can use when needed. ---- - ## Django Query Postprocessor This is the default postprocessor for the `use_query` hook. diff --git a/docs/src/get-started/choose-django-app.md b/docs/src/get-started/choose-django-app.md index 5f83fc2a..61dcfdba 100644 --- a/docs/src/get-started/choose-django-app.md +++ b/docs/src/get-started/choose-django-app.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" Set up a **Django Project** with at least one app. ---- - ## Choose a Django App If you have reached this point, you should have already [installed ReactPy-Django](../get-started/installation.md) through the previous steps. diff --git a/docs/src/get-started/create-component.md b/docs/src/get-started/create-component.md index e0516002..032906e7 100644 --- a/docs/src/get-started/create-component.md +++ b/docs/src/get-started/create-component.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" Create a component function using our decorator. ---- - ## Create a Component {% include-markdown "../../../README.md" start="" end="" %} diff --git a/docs/src/get-started/installation.md b/docs/src/get-started/installation.md index a27a3dc7..7aa2ea65 100644 --- a/docs/src/get-started/installation.md +++ b/docs/src/get-started/installation.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" ReactPy-Django can be installed from PyPI to an existing **Django project** with minimal configuration. ---- - ## Step 0: Create a Django Project These docs assumes you have already created [a **Django project**](https://docs.djangoproject.com/en/dev/intro/tutorial01/), which involves creating and installing at least one **Django app**. If not, check out this [9 minute YouTube tutorial](https://www.youtube.com/watch?v=ZsJRXS_vrw0) created by _IDG TECHtalk_. @@ -30,9 +28,11 @@ In your settings you will need to add `reactpy_django` to [`INSTALLED_APPS`](htt ReactPy-Django requires ASGI Websockets from [Django Channels](https://github.com/django/channels). - If you have not enabled ASGI on your **Django project** yet, you will need to install `channels[daphne]`, add `daphne` to `INSTALLED_APPS`, then set your `ASGI_APPLICATION` variable. + If you have not enabled ASGI on your **Django project** yet, you will need to - Read the [Django Channels Docs](https://channels.readthedocs.io/en/stable/installation.html) for more info. + 1. Install `channels[daphne]` + 2. Add `daphne` to `INSTALLED_APPS` + 3. Set your `ASGI_APPLICATION` variable. === "settings.py" @@ -40,11 +40,13 @@ In your settings you will need to add `reactpy_django` to [`INSTALLED_APPS`](htt {% include "../../python/configure-channels.py" %} ``` + Consider reading the [Django Channels Docs](https://channels.readthedocs.io/en/stable/installation.html) for more info. + ??? note "Configure ReactPy settings (Optional)" Below are a handful of values you can change within `settings.py` to modify the behavior of ReactPy. - ```python + ```python linenums="0" {% include "../../python/settings.py" %} ``` @@ -68,6 +70,20 @@ Register ReactPy's Websocket using `REACTPY_WEBSOCKET_PATH`. {% include "../../python/configure-asgi.py" %} ``` +??? note "Add `AuthMiddlewareStack` and `SessionMiddlewareStack` (Optional)" + + There are many situations where you need to access the Django `User` or `Session` objects within ReactPy components. For example, if you want to: + + 1. Access the `User` that is currently logged in + 2. Login or logout the current `User` + 3. Access Django's `Sesssion` object + + In these situations will need to ensure you are using `AuthMiddlewareStack` and/or `SessionMiddlewareStack`. + + ```python linenums="0" + {% include "../../python/configure-asgi-middleware.py" start="# start" %} + ``` + ??? question "Where is my `asgi.py`?" If you do not have an `asgi.py`, follow the [`channels` installation guide](https://channels.readthedocs.io/en/stable/installation.html). diff --git a/docs/src/get-started/learn-more.md b/docs/src/get-started/learn-more.md index f2cb52ae..8b58400d 100644 --- a/docs/src/get-started/learn-more.md +++ b/docs/src/get-started/learn-more.md @@ -8,4 +8,4 @@ Additionally, the vast majority of tutorials/guides you find for ReactJS can be === "Learn More" - [ReactPy-Django Advanced Usage](../features/components.md){ .md-button .md-button--primary} [ReactPy Core Documentation](https://reactpy.dev/docs/guides/creating-interfaces/index.html){ .md-button .md-button--primary } [Ask Questions](https://github.com/reactive-python/reactpy/discussions){ .md-button .md-button--primary } + [ReactPy-Django Advanced Usage](../features/components.md){ .md-button .md-button--primary} [ReactPy Core Documentation](https://reactpy.dev/docs/guides/creating-interfaces/index.html){ .md-button .md-button--primary } [Ask Questions on Discord](https://discord.gg/uNb5P4hA9X){ .md-button .md-button--primary } diff --git a/docs/src/get-started/register-view.md b/docs/src/get-started/register-view.md index 1c97d089..7f21bd66 100644 --- a/docs/src/get-started/register-view.md +++ b/docs/src/get-started/register-view.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" Select your template containing an ReactPy component, and render it using a Django view. ---- - ## Register a View We will assume you have [created a Django View](https://docs.djangoproject.com/en/dev/intro/tutorial01/#write-your-first-view) before, but here's a simple example below. diff --git a/docs/src/get-started/run-webserver.md b/docs/src/get-started/run-webserver.md index 1c532558..5a1d27dd 100644 --- a/docs/src/get-started/run-webserver.md +++ b/docs/src/get-started/run-webserver.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" Run a webserver to display your Django view. ---- - ## Run the Webserver To test your new Django view, run the following command to start up a development webserver. diff --git a/docs/src/get-started/use-template-tag.md b/docs/src/get-started/use-template-tag.md index 6fd9017c..e108e407 100644 --- a/docs/src/get-started/use-template-tag.md +++ b/docs/src/get-started/use-template-tag.md @@ -1,11 +1,9 @@ ## Overview -!!! summary +!!! summary "Overview" Decide where the component will be displayed by using our template tag. ---- - ## Use the Template Tag {% include-markdown "../../../README.md" start="" end="" %} diff --git a/docs/src/stylesheets/extra.css b/docs/src/stylesheets/extra.css index 72e8bd26..1599d525 100644 --- a/docs/src/stylesheets/extra.css +++ b/docs/src/stylesheets/extra.css @@ -31,11 +31,6 @@ border-color: transparent !important; } -[data-md-color-scheme="slate"] .md-typeset .admonition.summary, -[data-md-color-scheme="slate"] .md-typeset details.summary { - background: #353a45; -} - [data-md-color-scheme="slate"] .md-typeset details > .admonition-title:after, [data-md-color-scheme="slate"] .md-typeset details > summary:after { color: var(--md-admonition-fg-color) !important; @@ -45,6 +40,8 @@ [data-md-color-scheme="slate"] .md-typeset .admonition.summary, [data-md-color-scheme="slate"] .md-typeset details.summary { background: #353a45; + padding: 0.8rem 1.4rem; + border-radius: 0.8rem; } [data-md-color-scheme="slate"] .md-typeset details.summary > .admonition-title, @@ -54,7 +51,19 @@ [data-md-color-scheme="slate"] .md-typeset .summary .admonition-title, [data-md-color-scheme="slate"] .md-typeset .summary summary { + font-size: 1rem; background: transparent; + padding-left: 0.6rem; + padding-bottom: 0; +} + +[data-md-color-scheme="slate"] .md-typeset .summary .admonition-title:before { + display: none; +} + +[data-md-color-scheme="slate"] .md-typeset .admonition, +[data-md-color-scheme="slate"] .md-typeset details { + border-color: #ffffff17 !important; } /* Move the sidebars to the edges of the page */ diff --git a/mkdocs.yml b/mkdocs.yml index bca3df26..fa7b5f90 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,7 +21,8 @@ nav: - Code: contribute/code.md - Docs: contribute/docs.md - Running Tests: contribute/running-tests.md - - Community: https://github.com/reactive-python/reactpy/discussions + - GitHub Discussions: https://github.com/reactive-python/reactpy-django/discussions + - Discord: https://discord.gg/uNb5P4hA9X - Changelog: changelog/index.md theme: @@ -96,7 +97,7 @@ watch: site_name: ReactPy-Django Docs site_author: Archmonger site_description: React for Django developers. -copyright: Copyright © 2022 Reactive Python +copyright: Copyright © 2023 Reactive Python repo_url: https://github.com/reactive-python/reactpy-django site_url: https://reactive-python.github.io/reactpy-django repo_name: reactive-python/reactpy-django diff --git a/requirements/pkg-deps.txt b/requirements/pkg-deps.txt index 16b18367..75758695 100644 --- a/requirements/pkg-deps.txt +++ b/requirements/pkg-deps.txt @@ -1,4 +1,5 @@ channels >=4.0.0 +django >=4.1.0 reactpy >=1.0.0, <1.1.0 aiofile >=3.0 dill >=0.3.5 diff --git a/requirements/test-env.txt b/requirements/test-env.txt index f7552f1d..cd40cf23 100644 --- a/requirements/test-env.txt +++ b/requirements/test-env.txt @@ -1,4 +1,3 @@ -django playwright twisted channels[daphne]>=4.0.0 diff --git a/src/reactpy_django/__init__.py b/src/reactpy_django/__init__.py index 195626aa..0d8bb188 100644 --- a/src/reactpy_django/__init__.py +++ b/src/reactpy_django/__init__.py @@ -2,7 +2,7 @@ from reactpy_django.websocket.paths import REACTPY_WEBSOCKET_PATH -__version__ = "3.1.0" +__version__ = "3.2.0" __all__ = [ "REACTPY_WEBSOCKET_PATH", "hooks", diff --git a/src/reactpy_django/config.py b/src/reactpy_django/config.py index 33950b1e..960aa58f 100644 --- a/src/reactpy_django/config.py +++ b/src/reactpy_django/config.py @@ -50,3 +50,8 @@ ) ) ) +REACTPY_AUTH_BACKEND: str | None = getattr( + settings, + "REACTPY_AUTH_BACKEND", + None, +) diff --git a/src/reactpy_django/utils.py b/src/reactpy_django/utils.py index 86425552..da973a60 100644 --- a/src/reactpy_django/utils.py +++ b/src/reactpy_django/utils.py @@ -313,9 +313,15 @@ def create_cache_key(*args): def db_cleanup(immediate: bool = False): """Deletes expired component sessions from the database. This function may be expanded in the future to include additional cleanup tasks.""" - from .config import REACTPY_CACHE, REACTPY_DATABASE, REACTPY_RECONNECT_MAX + from .config import ( + REACTPY_CACHE, + REACTPY_DATABASE, + REACTPY_DEBUG_MODE, + REACTPY_RECONNECT_MAX, + ) from .models import ComponentSession + clean_started_at = datetime.now() cache_key: str = create_cache_key("last_cleaned") now_str: str = datetime.strftime(timezone.now(), DATE_FORMAT) cleaned_at_str: str = caches[REACTPY_CACHE].get(cache_key) @@ -340,3 +346,12 @@ def db_cleanup(immediate: bool = False): last_accessed__lte=expires_by ).delete() caches[REACTPY_CACHE].set(cache_key, now_str, timeout=None) + + # Check if cleaning took abnormally long + clean_duration = datetime.now() - clean_started_at + if REACTPY_DEBUG_MODE and clean_duration.total_seconds() > 1: + _logger.warning( + "ReactPy has taken %s seconds to clean up expired component sessions. " + "This may indicate a performance issue with your system, cache, or database.", + clean_duration.total_seconds(), + ) diff --git a/src/reactpy_django/websocket/consumer.py b/src/reactpy_django/websocket/consumer.py index 3679f00a..de8ec423 100644 --- a/src/reactpy_django/websocket/consumer.py +++ b/src/reactpy_django/websocket/consumer.py @@ -27,25 +27,43 @@ class ReactpyAsyncWebsocketConsumer(AsyncJsonWebsocketConsumer): """Communicates with the browser to perform actions on-demand.""" async def connect(self) -> None: - from django.contrib.auth.models import AbstractBaseUser - + """The browser has connected.""" await super().connect() - user: AbstractBaseUser = self.scope.get("user") + # Authenticate the user, if possible + from reactpy_django.config import REACTPY_AUTH_BACKEND + + user: Any = self.scope.get("user") if user and user.is_authenticated: try: - await login(self.scope, user) - await database_sync_to_async(self.scope["session"].save)() + await login(self.scope, user, backend=REACTPY_AUTH_BACKEND) except Exception: _logger.exception("ReactPy websocket authentication has failed!") elif user is None: - _logger.warning("ReactPy websocket is missing AuthMiddlewareStack!") + _logger.debug( + "ReactPy websocket is missing AuthMiddlewareStack! " + "Users will not be accessible within `use_scope` or `use_websocket`!" + ) + + # Save the session, if possible + if self.scope.get("session"): + try: + await database_sync_to_async(self.scope["session"].save)() + except Exception: + _logger.exception("ReactPy has failed to save scope['session']!") + else: + _logger.debug( + "ReactPy websocket is missing SessionMiddlewareStack! " + "Sessions will not be accessible within `use_scope` or `use_websocket`!" + ) + # Start allowing component renders self._reactpy_dispatcher_future = asyncio.ensure_future( self._run_dispatch_loop() ) async def disconnect(self, code: int) -> None: + """The browser has disconnected.""" if self._reactpy_dispatcher_future.done(): await self._reactpy_dispatcher_future else: @@ -53,9 +71,11 @@ async def disconnect(self, code: int) -> None: await super().disconnect(code) async def receive_json(self, content: Any, **_) -> None: + """Receive a message from the browser. Typically messages are event signals.""" await self._reactpy_recv_queue.put(content) async def _run_dispatch_loop(self): + """Runs the main loop that performs component rendering tasks.""" from reactpy_django import models from reactpy_django.config import ( REACTPY_DATABASE, @@ -77,7 +97,7 @@ async def _run_dispatch_loop(self): carrier=ComponentWebsocket(self.close, self.disconnect, dotted_path), ) now = timezone.now() - component_args: Sequence[Any] = tuple() + component_args: Sequence[Any] = () component_kwargs: MutableMapping[str, Any] = {} # Verify the component has already been registered @@ -130,7 +150,7 @@ async def _run_dispatch_loop(self): ) return - # Begin serving the ReactPy component + # Start the ReactPy component rendering loop try: await serve_layout( Layout(ConnectionContext(component_instance, value=connection)), diff --git a/tests/test_app/settings.py b/tests/test_app/settings.py index aa2c5196..15d6d1a1 100644 --- a/tests/test_app/settings.py +++ b/tests/test_app/settings.py @@ -170,3 +170,7 @@ }, }, } + + +# ReactPy Django Settings +REACTPY_AUTH_BACKEND = "django.contrib.auth.backends.ModelBackend"