diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 2a6bd62d..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2021 Ryan S. Morshead - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..f5423c3d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +## The MIT License (MIT) + +#### Copyright (c) Reactive Python and affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/docs/includes/orm.md b/docs/includes/orm.md index fafb6226..58f45477 100644 --- a/docs/includes/orm.md +++ b/docs/includes/orm.md @@ -2,12 +2,12 @@ Due to Django's ORM design, database queries must be deferred using hooks. Otherwise, you will see a `#!python SynchronousOnlyOperation` exception. -These `#!python SynchronousOnlyOperation` exceptions may be resolved in a future version of Django containing an asynchronous ORM. However, it is best practice to always perform ORM calls in the background via hooks. +These `#!python SynchronousOnlyOperation` exceptions may be removed in a future version of Django. However, it is best practice to always perform IO operations (such as ORM queries) via hooks to prevent performance issues. -By default, automatic recursive fetching of `#!python ManyToMany` or `#!python ForeignKey` fields is enabled within the default `#!python QueryOptions.postprocessor`. This is needed to prevent `#!python SynchronousOnlyOperation` exceptions when accessing these fields within your ReactPy components. +By default, automatic recursive fetching of `#!python ManyToMany` or `#!python ForeignKey` fields is enabled within the `django_query_postprocessor`. This is needed to prevent `#!python SynchronousOnlyOperation` exceptions when accessing these fields within your ReactPy components. diff --git a/docs/python/use-mutation-thread-sensitive.py b/docs/python/use-mutation-thread-sensitive.py new file mode 100644 index 00000000..ec07377e --- /dev/null +++ b/docs/python/use-mutation-thread-sensitive.py @@ -0,0 +1,32 @@ +from reactpy import component, html +from reactpy_django.hooks import use_mutation +from reactpy_django.types import MutationOptions + + +def execute_thread_safe_mutation(): + """This is an example mutation function that does some thread-safe operation.""" + pass + + +@component +def my_component(): + item_mutation = use_mutation( + MutationOptions(thread_sensitive=False), + execute_thread_safe_mutation, + ) + + def submit_event(event): + if event["key"] == "Enter": + item_mutation.execute(text=event["target"]["value"]) + + if item_mutation.loading or item_mutation.error: + mutation_status = html.h2("Doing something...") + elif item_mutation.error: + mutation_status = html.h2("Error!") + else: + mutation_status = html.h2("Done.") + + return html.div( + html.input({"type": "text", "onKeyDown": submit_event}), + mutation_status, + ) diff --git a/docs/python/use-mutation.py b/docs/python/use-mutation.py index 7c5c94fd..5b6f0e55 100644 --- a/docs/python/use-mutation.py +++ b/docs/python/use-mutation.py @@ -3,8 +3,8 @@ from reactpy_django.hooks import use_mutation -def add_item(text: str): - TodoItem(text=text).save() +async def add_item(text: str): + await TodoItem(text=text).asave() @component diff --git a/docs/python/use-query-async.py b/docs/python/use-query-async.py deleted file mode 100644 index e546ed59..00000000 --- a/docs/python/use-query-async.py +++ /dev/null @@ -1,22 +0,0 @@ -from channels.db import database_sync_to_async -from example.models import TodoItem -from reactpy import component, html -from reactpy_django.hooks import use_query - - -async def get_items(): - return await database_sync_to_async(TodoItem.objects.all)() - - -@component -def todo_list(): - item_query = use_query(get_items) - - if item_query.loading: - rendered_items = html.h2("Loading...") - elif item_query.error or not item_query.data: - rendered_items = html.h2("Error when loading!") - else: - rendered_items = html.ul([html.li(item, key=item) for item in item_query.data]) - - return html.div("Rendered items: ", rendered_items) diff --git a/docs/python/use-query.py b/docs/python/use-query.py index a8ed8b46..e546ed59 100644 --- a/docs/python/use-query.py +++ b/docs/python/use-query.py @@ -1,10 +1,11 @@ +from channels.db import database_sync_to_async from example.models import TodoItem from reactpy import component, html from reactpy_django.hooks import use_query -def get_items(): - return TodoItem.objects.all() +async def get_items(): + return await database_sync_to_async(TodoItem.objects.all)() @component diff --git a/docs/src/about/license.md b/docs/src/about/license.md new file mode 100644 index 00000000..15d975db --- /dev/null +++ b/docs/src/about/license.md @@ -0,0 +1,8 @@ +--- +hide: + - toc +--- + +--- + +{% include "../../../LICENSE.md" %} diff --git a/docs/src/assets/css/banner.css b/docs/src/assets/css/banner.css new file mode 100644 index 00000000..b3cb5ef4 --- /dev/null +++ b/docs/src/assets/css/banner.css @@ -0,0 +1,15 @@ +body[data-md-color-scheme="slate"] { + --md-banner-bg-color: #4d4121; + --md-banner-font-color: #fff; +} + +body[data-md-color-scheme="default"] { + --md-banner-bg-color: #ff9; + --md-banner-font-color: #000; +} + +.md-banner--warning { + background-color: var(--md-banner-bg-color); + color: var(--md-banner-font-color); + text-align: center; +} diff --git a/docs/src/assets/css/footer.css b/docs/src/assets/css/footer.css index 9dcaca2d..b3408286 100644 --- a/docs/src/assets/css/footer.css +++ b/docs/src/assets/css/footer.css @@ -27,3 +27,7 @@ .legal-footer-right { float: right; } + +.md-copyright__highlight div { + display: inline; +} diff --git a/docs/src/dictionary.txt b/docs/src/dictionary.txt index d3d2eb25..58f6eeec 100644 --- a/docs/src/dictionary.txt +++ b/docs/src/dictionary.txt @@ -37,3 +37,4 @@ frontends misconfiguration misconfigurations backhaul +sublicense diff --git a/docs/src/reference/components.md b/docs/src/reference/components.md index d3f235da..85199f27 100644 --- a/docs/src/reference/components.md +++ b/docs/src/reference/components.md @@ -177,7 +177,7 @@ Allows you to defer loading a CSS stylesheet until a component begins rendering. | Name | Type | Description | Default | | --- | --- | --- | --- | - | `#!python static_path` | `#!python str` | The path to the static file. This path is identical to what you would use on a `static` template tag. | N/A | + | `#!python static_path` | `#!python str` | The path to the static file. This path is identical to what you would use on Django's `#!jinja {% static %}` template tag. | N/A | | `#!python key` | `#!python Key | None` | A key to uniquely identify this component which is unique amongst a component's immediate siblings | `#!python None` | **Returns** @@ -186,10 +186,6 @@ Allows you to defer loading a CSS stylesheet until a component begins rendering. | --- | --- | | `#!python Component` | A ReactPy component. | -??? question "Should I put `#!python django_css` at the top of my HTML?" - - Yes, if the stylesheet contains styling for your component. - ??? question "Can I load static CSS using `#!python html.link` instead?" While you can load stylesheets with `#!python html.link`, keep in mind that loading this way **does not** ensure load order. Thus, your stylesheet will be loaded after your component is displayed. This would likely cause unintended visual behavior, so use this at your own discretion. @@ -204,7 +200,7 @@ Allows you to defer loading a CSS stylesheet until a component begins rendering. `#!python django_css` can only be used with local static files. - For external CSS, substitute `#!python django_css` with `#!python html.link`. + For external CSS, you should use `#!python html.link`. ```python {% include "../../python/django-css-external-link.py" %} @@ -212,14 +208,18 @@ Allows you to defer loading a CSS stylesheet until a component begins rendering. ??? question "Why not load my CSS in `#!html `?" - Traditionally, stylesheets are loaded in your `#!html ` using the `#!jinja {% load static %}` template tag. + Traditionally, stylesheets are loaded in your `#!html ` using Django's `#!jinja {% static %}` template tag. - To help improve webpage load times, you can use the `#!python django_css` component to defer loading your stylesheet until it is needed. + However, to help improve webpage load times you can use this `#!python django_css` component to defer loading your stylesheet until it is needed. ## Django JS Allows you to defer loading JavaScript until a component begins rendering. This JavaScript must be stored within [Django's static files](https://docs.djangoproject.com/en/dev/howto/static-files/). +!!! warning "Pitfall" + + Be mindful of load order! If your JavaScript relies on the component existing on the page, you must place `django_js` at the **bottom** of your component. + === "components.py" ```python @@ -232,7 +232,7 @@ Allows you to defer loading JavaScript until a component begins rendering. This | Name | Type | Description | Default | | --- | --- | --- | --- | - | `#!python static_path` | `#!python str` | The path to the static file. This path is identical to what you would use on a `static` template tag. | N/A | + | `#!python static_path` | `#!python str` | The path to the static file. This path is identical to what you would use on Django's `#!jinja {% static %}` template tag. | N/A | | `#!python key` | `#!python Key | None` | A key to uniquely identify this component which is unique amongst a component's immediate siblings | `#!python None` | **Returns** @@ -241,10 +241,6 @@ Allows you to defer loading JavaScript until a component begins rendering. This | --- | --- | | `#!python Component` | A ReactPy component. | -??? question "Should I put `#!python django_js` at the bottom of my HTML?" - - Yes, if your scripts are reliant on the contents of the component. - ??? question "Can I load static JavaScript using `#!python html.script` instead?" While you can load JavaScript with `#!python html.script`, keep in mind that loading this way **does not** ensure load order. Thus, your JavaScript will likely be loaded at an arbitrary time after your component is displayed. @@ -259,7 +255,7 @@ Allows you to defer loading JavaScript until a component begins rendering. This `#!python django_js` can only be used with local static files. - For external JavaScript, substitute `#!python django_js` with `#!python html.script`. + For external JavaScript, you should use `#!python html.script`. ```python {% include "../../python/django-js-remote-script.py" %} @@ -267,6 +263,6 @@ Allows you to defer loading JavaScript until a component begins rendering. This ??? question "Why not load my JS in `#!html `?" - Traditionally, JavaScript is loaded in your `#!html ` using the `#!jinja {% load static %}` template tag. + Traditionally, JavaScript is loaded in your `#!html ` using Django's `#!jinja {% static %}` template tag. - To help improve webpage load times, you can use the `#!python django_js` component to defer loading your JavaScript until it is needed. + However, to help improve webpage load times you can use this `#!python django_js` component to defer loading your JavaScript until it is needed. diff --git a/docs/src/reference/decorators.md b/docs/src/reference/decorators.md index 59440366..e5650648 100644 --- a/docs/src/reference/decorators.md +++ b/docs/src/reference/decorators.md @@ -10,7 +10,7 @@ Decorator functions can be used within your `components.py` to help simplify dev ## Auth Required -You can limit access to a component to users with a specific `#!python auth_attribute` by using this decorator (with or without parentheses). +You can limit component access to users with a specific `#!python auth_attribute` by using this decorator (with or without parentheses). By default, this decorator checks if the user is logged in and not deactivated (`#!python is_active`). diff --git a/docs/src/reference/hooks.md b/docs/src/reference/hooks.md index 3233a5bb..c9b3985d 100644 --- a/docs/src/reference/hooks.md +++ b/docs/src/reference/hooks.md @@ -16,9 +16,9 @@ Prefabricated hooks can be used within your `components.py` to help simplify dev ## Use Query -This hook is used [read](https://www.sumologic.com/glossary/crud/) data from the Django ORM. +This hook is used to execute functions in the background and return the result, typically to [read](https://www.sumologic.com/glossary/crud/) data the Django ORM. -The query function you provide must return either a `#!python Model` or `#!python QuerySet`. +The [default postprocessor](../reference/utils.md#django-query-postprocessor) expects your query function to `#!python return` a Django `#!python Model` or `#!python QuerySet`. The postprocessor needs to be changed to execute other types of queries. Query functions can be sync or async. === "components.py" @@ -59,13 +59,13 @@ The query function you provide must return either a `#!python Model` or `#!pytho {% include "../../python/use-query-args.py" %} ``` -??? question "Why does `#!python get_items` in the example return `#!python TodoItem.objects.all()`?" +??? question "How can I customize this hook's behavior?" - This was a technical design decision to based on [Apollo's `#!javascript useQuery` hook](https://www.apollographql.com/docs/react/data/queries/), but ultimately helps avoid Django's `#!python SynchronousOnlyOperation` exceptions. + This hook accepts a `#!python options: QueryOptions` parameter that can be used to customize behavior. - The `#!python use_query` hook ensures the provided `#!python Model` or `#!python QuerySet` executes all [deferred](https://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.get_deferred_fields)/[lazy queries](https://docs.djangoproject.com/en/dev/topics/db/queries/#querysets-are-lazy) safely prior to reaching your components. + Below are the settings that can be modified via these `#!python QueryOptions`. -??? question "How can I use `#!python QueryOptions` to customize fetching behavior?" + --- **`#!python thread_sensitive`** @@ -128,27 +128,39 @@ The query function you provide must return either a `#!python Model` or `#!pytho _Note: In Django's ORM design, the field name to access foreign keys is [postfixed with `_set`](https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_one/) by default._ -??? question "Can I define async query functions?" +??? question "Can I make ORM calls without hooks?" + + {% include-markdown "../../includes/orm.md" start="" end="" %} - Async functions are supported by `#!python use_query`. You can use them in the same way as a sync query function. +??? question "Can I make a failed query try again?" - However, be mindful of Django async ORM restrictions. + Yes, a `#!python use_mutation` can be re-performed by calling `#!python reset()` on your `#!python use_mutation` instance. + + For example, take a look at `#!python reset_event` below. === "components.py" ```python - {% include "../../python/use-query-async.py" %} + {% include "../../python/use-mutation-reset.py" %} ``` -??? question "Can I make ORM calls without hooks?" + === "models.py" - {% include-markdown "../../includes/orm.md" start="" end="" %} + ```python + {% include "../../python/example/models.py" %} + ``` + +??? question "Why does the example query function return `#!python TodoItem.objects.all()`?" + + This design decision was based on [Apollo's `#!javascript useQuery` hook](https://www.apollographql.com/docs/react/data/queries/), but ultimately helps avoid Django's `#!python SynchronousOnlyOperation` exceptions. + + With the `#!python Model` or `#!python QuerySet` your function returns, this hook uses the [default postprocessor](../reference/utils.md#django-query-postprocessor) to ensure that all [deferred](https://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.get_deferred_fields) or [lazy](https://docs.djangoproject.com/en/dev/topics/db/queries/#querysets-are-lazy) fields are executed. ## Use Mutation -This hook is used to [create, update, or delete](https://www.sumologic.com/glossary/crud/) Django ORM objects. +This hook is used to modify data in the background, typically to [create/update/delete](https://www.sumologic.com/glossary/crud/) data from the Django ORM. -The mutation function you provide should have no return value. +Mutation functions can `#!python return False` to prevent executing your `#!python refetch` function. All other returns are ignored. Mutation functions can be sync or async. === "components.py" @@ -169,7 +181,7 @@ The mutation function you provide should have no return value. | Name | Type | Description | Default | | --- | --- | --- | --- | | `#!python mutate` | `#!python Callable[_Params, bool | None]` | A callable that performs Django ORM create, update, or delete functionality. If this function returns `#!python False`, then your `#!python refetch` function will not be used. | N/A | - | `#!python refetch` | `#!python Callable[..., Any] | Sequence[Callable[..., Any]] | None` | A `#!python query` function (used by the `#!python use_query` hook) or a sequence of `#!python query` functions that will be called if the mutation succeeds. This is useful for refetching data after a mutation has been performed. | `#!python None` | + | `#!python refetch` | `#!python Callable[..., Any] | Sequence[Callable[..., Any]] | None` | A query function (the function you provide to your `#!python use_query` hook) or a sequence of query functions that need a `refetch` if the mutation succeeds. This is useful for refreshing data after a mutation has been performed. | `#!python None` | **Returns** @@ -187,27 +199,31 @@ The mutation function you provide should have no return value. {% include "../../python/use-mutation-args-kwargs.py" %} ``` -??? question "Can `#!python use_mutation` trigger a refetch of `#!python use_query`?" +??? question "How can I customize this hook's behavior?" - Yes, `#!python use_mutation` can queue a refetch of a `#!python use_query` via the `#!python refetch=...` argument. + This hook accepts a `#!python options: MutationOptions` parameter that can be used to customize behavior. - The example below is a merge of the `#!python use_query` and `#!python use_mutation` examples above with the addition of a `#!python use_mutation(refetch=...)` argument. + Below are the settings that can be modified via these `#!python MutationOptions`. - Please note that any `#!python use_query` hooks that use `#!python get_items` will be refetched upon a successful mutation. + --- + + **`#!python thread_sensitive`** + + Whether to run your synchronous mutation function in thread-sensitive mode. Thread-sensitive mode is turned on by default due to Django ORM limitations. See Django's [`#!python sync_to_async` docs](https://docs.djangoproject.com/en/dev/topics/async/#sync-to-async) docs for more information. + + This setting only applies to sync query functions, and will be ignored for async functions. === "components.py" ```python - {% include "../../python/use-mutation-query-refetch.py" %} + {% include "../../python/use-mutation-thread-sensitive.py" %} ``` - === "models.py" +??? question "Can I make ORM calls without hooks?" - ```python - {% include "../../python/example/models.py" %} - ``` + {% include-markdown "../../includes/orm.md" start="" end="" %} -??? question "Can I make a failed `#!python use_mutation` try again?" +??? question "Can I make a failed mutation try again?" Yes, a `#!python use_mutation` can be re-performed by calling `#!python reset()` on your `#!python use_mutation` instance. @@ -225,13 +241,29 @@ The mutation function you provide should have no return value. {% include "../../python/example/models.py" %} ``` -??? question "Can I make ORM calls without hooks?" +??? question "Can `#!python use_mutation` trigger a refetch of `#!python use_query`?" - {% include-markdown "../../includes/orm.md" start="" end="" %} + Yes, `#!python use_mutation` can queue a refetch of a `#!python use_query` via the `#!python refetch=...` argument. + + The example below is a merge of the `#!python use_query` and `#!python use_mutation` examples above with the addition of a `#!python use_mutation(refetch=...)` argument. + + Please note that `refetch` will cause all `#!python use_query` hooks that use `#!python get_items` in the current component tree will be refetched. + + === "components.py" + + ```python + {% include "../../python/use-mutation-query-refetch.py" %} + ``` + + === "models.py" + + ```python + {% include "../../python/example/models.py" %} + ``` ## Use Connection -This hook is used to fetch the Django Channels [WebSocket](https://channels.readthedocs.io/en/stable/topics/consumers.html#asyncjsonwebsocketconsumer). +This hook is used to fetch the active connection, which is either a Django [WebSocket](https://channels.readthedocs.io/en/stable/topics/consumers.html#asyncjsonwebsocketconsumer) or a [HTTP Request](https://docs.djangoproject.com/en/4.2/ref/request-response/#django.http.HttpRequest). === "components.py" @@ -249,11 +281,11 @@ This hook is used to fetch the Django Channels [WebSocket](https://channels.read | Type | Description | | --- | --- | - | `#!python Connection` | The component's WebSocket. | + | `#!python Connection` | The component's `WebSocket` or `HttpRequest`. | ## Use Scope -This is a shortcut that returns the WebSocket's [`#!python scope`](https://channels.readthedocs.io/en/stable/topics/consumers.html#scope). +This is a shortcut that returns the WebSocket or HTTP [scope](https://channels.readthedocs.io/en/stable/topics/consumers.html#scope). === "components.py" @@ -275,7 +307,7 @@ This is a shortcut that returns the WebSocket's [`#!python scope`](https://chann ## Use Location -This is a shortcut that returns the WebSocket's `#!python path`. +This is a shortcut that returns the client's URL `#!python path`. You can expect this hook to provide strings such as `/reactpy/my_path`. @@ -305,7 +337,7 @@ You can expect this hook to provide strings such as `/reactpy/my_path`. ## Use Origin -This is a shortcut that returns the WebSocket's `#!python origin`. +This is a shortcut that returns the client's `#!python origin`. You can expect this hook to provide strings such as `http://example.com`. @@ -325,4 +357,4 @@ You can expect this hook to provide strings such as `http://example.com`. | Type | Description | | --- | --- | - | `#!python str | None` | A string containing the browser's current origin, obtained from WebSocket headers (if available). | + | `#!python str | None` | A string containing the browser's current origin, obtained from WebSocket or HTTP headers (if available). | diff --git a/docs/src/reference/settings.md b/docs/src/reference/settings.md index 35f65b6e..46ed7070 100644 --- a/docs/src/reference/settings.md +++ b/docs/src/reference/settings.md @@ -34,7 +34,7 @@ The prefix used for all ReactPy WebSocket and HTTP URLs. **Example Value(s):** `#!python "example_project.postprocessor"`, `#!python None` -Dotted path to the default `#!python reactpy_django.hooks.use_query` postprocessor function. +Dotted path to the global default `#!python reactpy_django.hooks.use_query` postprocessor function. Postprocessor functions can be async or sync. Here is an example of a sync postprocessor function: diff --git a/docs/src/reference/template-tag.md b/docs/src/reference/template-tag.md index 39974eb7..6321243b 100644 --- a/docs/src/reference/template-tag.md +++ b/docs/src/reference/template-tag.md @@ -40,7 +40,7 @@ This template tag can be used to insert any number of ReactPy components onto yo ??? warning "Do not use context variables for the component path" - The ReactPy component finder (`#!python reactpy_django.utils.RootComponentFinder`) requires that your component path is a string. + The ReactPy component finder requires that your component path is a string. **Do not** use Django template/context variables for the component path. Failure to follow this warning can result in unexpected behavior, such as components that will not render. @@ -81,7 +81,7 @@ This template tag can be used to insert any number of ReactPy components onto yo Here's a couple of things to keep in mind: 1. If your host address are completely separate ( `origin1.com != origin2.com` ) you will need to [configure CORS headers](https://pypi.org/project/django-cors-headers/) on your main application during deployment. - 2. You will not need to register ReactPy HTTP or WebSocket paths on any applications that do not perform any component rendering. + 2. You will not need to register ReactPy WebSocket or HTTP paths on any applications that do not perform any component rendering. 3. Your component will only be able to access your template tag's `#!python *args`/`#!python **kwargs` if your applications share a common database. diff --git a/docs/src/reference/utils.md b/docs/src/reference/utils.md index e5c10057..d9b36462 100644 --- a/docs/src/reference/utils.md +++ b/docs/src/reference/utils.md @@ -60,6 +60,6 @@ This function is used manually register a root component with ReactPy. You typically will not need to use this function. - For security reasons, ReactPy does not allow non-registered components to be root components. However, all components contained within Django templates are automatically considered root components. + For security reasons, ReactPy requires all root components to be registered. However, all components contained within Django templates are automatically registered. - This is typically only needed when you have a dedicated Django application as a rendering server that doesn't have templates, such as when modifying the [template tag `#!python host` argument](../reference/template-tag.md#component). On this dedicated rendering server, you would need to manually register your components. + This function is needed when you have configured your [`host`](../reference/template-tag.md#component) to a dedicated Django rendering application that doesn't have templates. diff --git a/mkdocs.yml b/mkdocs.yml index af88f66b..35bb6bb0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -12,12 +12,14 @@ nav: - Template Tag: reference/template-tag.md - Settings: reference/settings.md - About: - - Contribute: + - Changelog: about/changelog.md + - Contributor Guide: - Code: about/code.md - Docs: about/docs.md - - GitHub Discussions: https://github.com/reactive-python/reactpy-django/discussions - - Discord: https://discord.gg/uNb5P4hA9X - - Changelog: about/changelog.md + - Community: + - GitHub Discussions: https://github.com/reactive-python/reactpy-django/discussions + - Discord: https://discord.gg/uNb5P4hA9X + - License: about/license.md theme: name: material @@ -90,6 +92,9 @@ extra: generator: false version: provider: mike + analytics: + provider: google + property: G-4KFQN2LWBG extra_javascript: - assets/js/main.js @@ -98,6 +103,7 @@ extra_css: - assets/css/main.css - assets/css/button.css - assets/css/admonition.css + - assets/css/banner.css - assets/css/sidebar.css - assets/css/navbar.css - assets/css/table-of-contents.css @@ -110,12 +116,19 @@ watch: - mkdocs.yml - README.md - CHANGELOG.md + - LICENSE.md - .mailmap site_name: ReactPy-Django site_author: Archmonger site_description: It's React, but in Python. Now with Django integration. -copyright: Copyright © 2023 Reactive Python. +copyright: '© +
+ +Reactive Python and affiliates. +' 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/src/reactpy_django/components.py b/src/reactpy_django/components.py index 31df3e2e..6884276b 100644 --- a/src/reactpy_django/components.py +++ b/src/reactpy_django/components.py @@ -124,16 +124,16 @@ def view_to_component( Keyword Args: view: The view function or class to convert. - compatibility: If True, the component will be rendered in an iframe. - When using compatibility mode `tranforms`, `strict_parsing`, `request`, + compatibility: If True, the component will be rendered in an iframe. \ + When using compatibility mode `tranforms`, `strict_parsing`, `request`, \ `args, and `kwargs` arguments will be ignored. - transforms: A list of functions that transforms the newly generated VDOM. + transforms: A list of functions that transforms the newly generated VDOM. \ The functions will be called on each VDOM node. - strict_parsing: If True, an exception will be generated if the HTML does not + strict_parsing: If True, an exception will be generated if the HTML does not \ perfectly adhere to HTML5. Returns: - A function that takes `request: HttpRequest | None, *args: Any, key: Key | None, **kwargs: Any` + A function that takes `request: HttpRequest | None, *args: Any, key: Key | None, **kwargs: Any` \ and returns a ReactPy component. """ @@ -172,9 +172,9 @@ def django_css(static_path: str, key: Key | None = None): """Fetches a CSS static file for use within ReactPy. This allows for deferred CSS loading. Args: - static_path: The path to the static file. This path is identical to what you would - use on a `static` template tag. - key: A key to uniquely identify this component which is unique amongst a component's + static_path: The path to the static file. This path is identical to what you would \ + use on Django's `{% static %}` template tag + key: A key to uniquely identify this component which is unique amongst a component's \ immediate siblings """ @@ -190,9 +190,9 @@ def django_js(static_path: str, key: Key | None = None): """Fetches a JS static file for use within ReactPy. This allows for deferred JS loading. Args: - static_path: The path to the static file. This path is identical to what you would - use on a `static` template tag. - key: A key to uniquely identify this component which is unique amongst a component's + static_path: The path to the static file. This path is identical to what you would \ + use on Django's `{% static %}` template tag. + key: A key to uniquely identify this component which is unique amongst a component's \ immediate siblings """ diff --git a/src/reactpy_django/decorators.py b/src/reactpy_django/decorators.py index fc24fdfd..4f3befe2 100644 --- a/src/reactpy_django/decorators.py +++ b/src/reactpy_django/decorators.py @@ -19,8 +19,8 @@ def auth_required( This decorator can be used with or without parentheses. Args: - auth_attribute: The value to check within the user object. - This is checked in the form of `UserModel.`. + auth_attribute: The value to check within the user object. \ + This is checked in the form of `UserModel.`. \ fallback: The component or VDOM (`reactpy.html` snippet) to render if the user is not authenticated. """ diff --git a/src/reactpy_django/hooks.py b/src/reactpy_django/hooks.py index 8115de56..2fbdd88f 100644 --- a/src/reactpy_django/hooks.py +++ b/src/reactpy_django/hooks.py @@ -229,12 +229,13 @@ def use_mutation(*args: Any, **kwargs: Any) -> Mutation[_Params]: """Hook to create, update, or delete Django ORM objects. Args: - mutate: A callable that performs Django ORM create, update, or delete - functionality. If this function returns `False`, then your `refetch` + mutation: A callable that performs Django ORM create, update, or delete \ + functionality. If this function returns `False`, then your `refetch` \ function will not be used. - refetch: A `query` function (used by the `use_query` hook) or a sequence of `query` - functions that will be called if the mutation succeeds. This is useful for - refetching data after a mutation has been performed. + refetch: A query function (the function you provide to your `use_query` \ + hook) or a sequence of query functions that need a `refetch` if the \ + mutation succeeds. This is useful for refreshing data after a mutation \ + has been performed. """ loading, set_loading = use_state(False)