From ccf02fa316536eb7d991a2723676da4978b0cdf0 Mon Sep 17 00:00:00 2001 From: Juro Oravec Date: Sun, 11 May 2025 14:59:34 +0200 Subject: [PATCH] chore: util to manage URLs in the codebase (#1179) * chore: util to manage URLs in the codebase * docs: mentiion validate_links and supported_versions in docs * refactor: fix linter errors --- .gitignore | 8 + CHANGELOG.md | 10 +- README.md | 2 +- benchmarks/README.md | 2 +- .../advanced/component_context_scope.md | 8 +- docs/concepts/advanced/component_libraries.md | 14 +- docs/concepts/advanced/extensions.md | 8 +- docs/concepts/advanced/provide_inject.md | 2 - docs/concepts/advanced/rendering_js_css.md | 8 +- docs/concepts/fundamentals/autodiscovery.md | 2 +- .../fundamentals/component_views_urls.md | 10 +- docs/concepts/fundamentals/http_request.md | 2 +- docs/concepts/fundamentals/render_api.md | 12 +- .../fundamentals/rendering_components.md | 50 +-- .../fundamentals/secondary_js_css_files.md | 28 +- docs/getting_started/adding_js_and_css.md | 4 +- .../parametrising_components.md | 2 +- docs/getting_started/rendering_components.md | 26 +- docs/guides/other/troubleshooting.md | 4 +- docs/guides/setup/caching.md | 2 +- docs/overview/compatibility.md | 2 +- docs/overview/development.md | 31 ++ docs/overview/installation.md | 4 +- docs/overview/security_notes.md | 6 +- docs/overview/welcome.md | 2 +- docs/scripts/reference.py | 4 +- docs/templates/reference_commands.md | 2 +- docs/templates/reference_signals.md | 2 +- mkdocs.yml | 4 +- requirements-ci.in | 3 +- requirements-ci.txt | 2 + requirements-dev.in | 3 +- requirements-dev.txt | 2 +- sampleproject/sampleproject/asgi.py | 2 +- sampleproject/sampleproject/settings.py | 12 +- sampleproject/sampleproject/wsgi.py | 2 +- scripts/validate_links.py | 406 ++++++++++++++++++ src/django_components/app_settings.py | 18 +- src/django_components/commands/list.py | 54 +-- src/django_components/component.py | 33 +- src/django_components/component_registry.py | 6 +- src/django_components/library.py | 4 +- src/django_components/node.py | 4 +- src/django_components/template.py | 6 +- .../templatetags/component_tags.py | 2 +- src/django_components/util/command.py | 2 +- src/django_components/util/loader.py | 2 +- src/django_components/util/misc.py | 52 +++ src/django_components/util/routing.py | 2 +- src/django_components/util/tag_parser.py | 2 +- src/django_components/util/testing.py | 2 +- tests/components/glob/glob.py | 6 +- tests/e2e/testserver/testserver/asgi.py | 2 +- tests/e2e/testserver/testserver/settings.py | 8 +- tests/e2e/testserver/testserver/wsgi.py | 2 +- tests/static_root/staticfiles.json | 2 +- tests/test_cache.py | 12 - tests/test_component_cache.py | 5 - tests/test_component_media.py | 14 +- tests/test_context.py | 16 - tests/test_dependencies.py | 18 - tests/test_dependency_rendering.py | 9 - tests/test_expression.py | 2 - tests/test_tag_parser.py | 3 - tests/test_templatetags_provide.py | 3 - tests/test_templatetags_slot_fill.py | 1 - tox.ini | 2 +- 67 files changed, 678 insertions(+), 309 deletions(-) create mode 100644 scripts/validate_links.py diff --git a/.gitignore b/.gitignore index 50cb9c64..3d7aeebe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Project-specific files +sampleproject/staticfiles/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -43,6 +46,7 @@ htmlcov/ nosetests.xml coverage.xml *,cover +.pytest_cache/ # Translations *.mo @@ -76,7 +80,11 @@ poetry.lock site .direnv/ .envrc +.mypy_cache/ # JS, NPM Dependency directories node_modules/ jspm_packages/ + +# Cursor +.cursorrules \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cf521c5..bf8b426d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -718,7 +718,7 @@ where each class name or style property can be managed separately. - If using `pytest`, the decorator allows you to parametrize Django or Components settings. - The decorator also serves as a stand-in for Django's `@override_settings`. - See the API reference for [`@djc_test`](https://django-components.github.io/django-components/0.131/reference/testing_api/#djc_test) for more details. + See the API reference for [`@djc_test`](https://django-components.github.io/django-components/0.131/reference/testing_api/#django_components.testing.djc_test) for more details. - `ComponentRegistry` now has a `has()` method to check if a component is registered without raising an error. @@ -920,12 +920,12 @@ If you see any broken links or other issues, please report them in [#922](https: - Component inheritance: - When you subclass a component, the JS and CSS defined on parent's `Media` class is now inherited by the child component. - - You can disable or customize Media inheritance by setting `extend` attribute on the `Component.Media` nested class. This work similarly to Django's [`Media.extend`](https://docs.djangoproject.com/en/5.1/topics/forms/media/#extend). + - You can disable or customize Media inheritance by setting `extend` attribute on the `Component.Media` nested class. This work similarly to Django's [`Media.extend`](https://docs.djangoproject.com/en/5.2/topics/forms/media/#extend). - When child component defines either `template` or `template_file`, both of parent's `template` and `template_file` are ignored. The same applies to `js_file` and `css_file`. - Autodiscovery now ignores files and directories that start with an underscore (`_`), except `__init__.py` -- The [Signals](https://docs.djangoproject.com/en/5.1/topics/signals/) emitted by or during the use of django-components are now documented, together the `template_rendered` signal. +- The [Signals](https://docs.djangoproject.com/en/5.2/topics/signals/) emitted by or during the use of django-components are now documented, together the `template_rendered` signal. ## v0.123 @@ -937,7 +937,7 @@ If you see any broken links or other issues, please report them in [#922](https: #### Feat -- Add support for HTML fragments. HTML fragments can be rendered by passing `type="fragment"` to `Component.render()` or `Component.render_to_response()`. Read more on how to [use HTML fragments with HTMX, AlpineJS, or vanillaJS](https://django-components.github.io/django-components/latest/concepts/advanced/html_tragments). +- Add support for HTML fragments. HTML fragments can be rendered by passing `type="fragment"` to `Component.render()` or `Component.render_to_response()`. Read more on how to [use HTML fragments with HTMX, AlpineJS, or vanillaJS](https://django-components.github.io/django-components/latest/concepts/advanced/html_fragments). ## v0.121 @@ -1555,7 +1555,7 @@ importing them. - `SETTINGS_MODULE` - Define component dirs using `STATICFILES_DIRS` - - Previously, autodiscovery handled relative files in `STATICFILES_DIRS`. To align with Django, `STATICFILES_DIRS` now must be full paths ([Django docs](https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-STATICFILES_DIRS)). + - Previously, autodiscovery handled relative files in `STATICFILES_DIRS`. To align with Django, `STATICFILES_DIRS` now must be full paths ([Django docs](https://docs.djangoproject.com/en/5.2/ref/settings/#std-setting-STATICFILES_DIRS)). ## 🚨📢 v0.81 diff --git a/README.md b/README.md index 5c59a4f1..d899956e 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ class Calendar(Component): # Additional JS and CSS class Media: - js = ["https://cdn.jsdelivr.net/npm/htmx.org@2.1.1/dist/htmx.min.js"] + js = ["https://cdn.jsdelivr.net/npm/htmx.org@2/dist/htmx.min.js"] css = ["bootstrap/dist/css/bootstrap.min.css"] # Variables available in the template diff --git a/benchmarks/README.md b/benchmarks/README.md index 4ef8dc8d..f5f5d524 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -26,7 +26,7 @@ django-components uses `asv` for these use cases: 1. When a git tag is created and pushed, we also update the documentation website (see `docs.yml`). 2. Before we publish the docs website, we generate the HTML report for the benchmark results. 3. The generated report is placed in the `docs/benchmarks/` directory, and is thus - published with the rest of the docs website and available under [`/benchmarks/`](https://django-components.github.io/django-components/benchmarks). + published with the rest of the docs website and available under [`/benchmarks/`](https://django-components.github.io/django-components/latest/benchmarks). - NOTE: The location where the report is placed is defined in `asv.conf.json`. - Compare performance between commits on pull requests: diff --git a/docs/concepts/advanced/component_context_scope.md b/docs/concepts/advanced/component_context_scope.md index 418a1d69..8d4ad4ca 100644 --- a/docs/concepts/advanced/component_context_scope.md +++ b/docs/concepts/advanced/component_context_scope.md @@ -53,8 +53,8 @@ This has two modes: you can access are a union of: - All the variables that were OUTSIDE the fill tag, including any\ - [`{% with %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#with) tags. - - Any loops ([`{% for ... %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#cycle)) + [`{% with %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#with) tags. + - Any loops ([`{% for ... %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#cycle)) that the `{% fill %}` tag is part of. - Data returned from [`Component.get_template_data()`](../../../reference/api#django_components.Component.get_template_data) of the component that owns the fill tag. @@ -67,7 +67,7 @@ This has two modes: Inside the [`{% fill %}`](../../../reference/template_tags#fill) tag, you can ONLY access variables from 2 places: - - Any loops ([`{% for ... %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#cycle)) + - Any loops ([`{% for ... %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#cycle)) that the `{% fill %}` tag is part of. - [`Component.get_template_data()`](../../../reference/api#django_components.Component.get_template_data) of the component which defined the template (AKA the "root" component). @@ -177,5 +177,5 @@ But since `"cheese"` is not defined there, it's empty. !!! info - Notice that the variables defined with the [`{% with %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#with) + Notice that the variables defined with the [`{% with %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#with) tag are ignored inside the [`{% fill %}`](../../../reference/template_tags#fill) tag with the `"isolated"` mode. diff --git a/docs/concepts/advanced/component_libraries.md b/docs/concepts/advanced/component_libraries.md index 2ed33a91..44aa4577 100644 --- a/docs/concepts/advanced/component_libraries.md +++ b/docs/concepts/advanced/component_libraries.md @@ -23,13 +23,13 @@ For live examples, see the [Community examples](../../overview/community.md#comm |-- mytags.py ``` -2. Create custom [`Library`](https://docs.djangoproject.com/en/5.1/howto/custom-template-tags/#how-to-create-custom-template-tags-and-filters) +2. Create custom [`Library`](https://docs.djangoproject.com/en/5.2/howto/custom-template-tags/#how-to-create-custom-template-tags-and-filters) and [`ComponentRegistry`](django_components.component_registry.ComponentRegistry) instances in `mytags.py` This will be the entrypoint for using the components inside Django templates. - Remember that Django requires the [`Library`](https://docs.djangoproject.com/en/5.1/howto/custom-template-tags/#how-to-create-custom-template-tags-and-filters) - instance to be accessible under the `register` variable ([See Django docs](https://docs.djangoproject.com/en/dev/howto/custom-template-tags)): + Remember that Django requires the [`Library`](https://docs.djangoproject.com/en/5.2/howto/custom-template-tags/#how-to-create-custom-template-tags-and-filters) + instance to be accessible under the `register` variable ([See Django docs](https://docs.djangoproject.com/en/5.2/howto/custom-template-tags)): ```py from django.template import Library @@ -148,7 +148,7 @@ For live examples, see the [Community examples](../../overview/community.md#comm Since you, as the library author, are not in control of the file system, it is recommended to load the components manually. - We recommend doing this in the [`AppConfig.ready()`](https://docs.djangoproject.com/en/5.1/ref/applications/#django.apps.AppConfig.ready) + We recommend doing this in the [`AppConfig.ready()`](https://docs.djangoproject.com/en/5.2/ref/applications/#django.apps.AppConfig.ready) hook of your `apps.py`: ```py @@ -170,7 +170,7 @@ For live examples, see the [Community examples](../../overview/community.md#comm ``` Note that you can also include any other startup logic within - [`AppConfig.ready()`](https://docs.djangoproject.com/en/5.1/ref/applications/#django.apps.AppConfig.ready). + [`AppConfig.ready()`](https://docs.djangoproject.com/en/5.2/ref/applications/#django.apps.AppConfig.ready). And that's it! The next step is to publish it. @@ -185,7 +185,7 @@ django_components uses the [`build`](https://build.pypa.io/en/stable/) utility t python -m build --sdist --wheel --outdir dist/ . ``` -And to publish to PyPI, you can use [`twine`](https://docs.djangoproject.com/en/5.1/ref/applications/#django.apps.AppConfig.ready) +And to publish to PyPI, you can use [`twine`](https://docs.djangoproject.com/en/5.2/ref/applications/#django.apps.AppConfig.ready) ([See Python user guide](https://packaging.python.org/en/latest/tutorials/packaging-projects/#uploading-the-distribution-archives)) ```bash @@ -219,7 +219,7 @@ After the package has been published, all that remains is to install it in other ] ``` -3. Optionally add the template tags to the [`builtins`](https://docs.djangoproject.com/en/5.1/topics/templates/#django.template.backends.django.DjangoTemplates), +3. Optionally add the template tags to the [`builtins`](https://docs.djangoproject.com/en/5.2/topics/templates/#django.template.backends.django.DjangoTemplates), so you don't have to call `{% load mytags %}` in every template: ```python diff --git a/docs/concepts/advanced/extensions.md b/docs/concepts/advanced/extensions.md index dad769e6..241c998e 100644 --- a/docs/concepts/advanced/extensions.md +++ b/docs/concepts/advanced/extensions.md @@ -624,7 +624,7 @@ The help message prints out all the arguments and options available for the comm ### Testing Commands -Commands can be tested using Django's [`call_command()`](https://docs.djangoproject.com/en/5.1/ref/django-admin/#running-management-commands-from-your-code) +Commands can be tested using Django's [`call_command()`](https://docs.djangoproject.com/en/5.2/ref/django-admin/#running-management-commands-from-your-code) function, which allows you to simulate running the command in tests. ```python @@ -699,8 +699,8 @@ class MyExtension(ComponentExtension): The [`URLRoute`](../../../reference/extension_urls#django_components.URLRoute) objects are different from objects created with Django's - [`django.urls.path()`](https://docs.djangoproject.com/en/5.1/ref/urls/#path). - Do NOT use `URLRoute` objects in Django's [`urlpatterns`](https://docs.djangoproject.com/en/5.1/topics/http/urls/#example) + [`django.urls.path()`](https://docs.djangoproject.com/en/5.2/ref/urls/#path). + Do NOT use `URLRoute` objects in Django's [`urlpatterns`](https://docs.djangoproject.com/en/5.2/topics/http/urls/#example) and vice versa! django-components uses a custom [`URLRoute`](../../../reference/extension_urls#django_components.URLRoute) class to define framework-agnostic routing rules. @@ -758,7 +758,7 @@ The [`URLRoute`](../../../reference/extension_urls#django_components.URLRoute) c so that extensions could be used with non-Django frameworks in the future. However, that means that there may be some extra fields that Django's -[`django.urls.path()`](https://docs.djangoproject.com/en/5.1/ref/urls/#path) +[`django.urls.path()`](https://docs.djangoproject.com/en/5.2/ref/urls/#path) accepts, but which are not defined on the `URLRoute` object. To address this, the [`URLRoute`](../../../reference/extension_urls#django_components.URLRoute) object has diff --git a/docs/concepts/advanced/provide_inject.md b/docs/concepts/advanced/provide_inject.md index 3c843f5e..62fe32de 100644 --- a/docs/concepts/advanced/provide_inject.md +++ b/docs/concepts/advanced/provide_inject.md @@ -82,7 +82,6 @@ class ChildComponent(Component): my_data = self.inject("my_data") print(my_data.hello) # hi print(my_data.another) # 123 - return {} ``` First argument to [`Component.inject()`](../../../reference/api/#django_components.Component.inject) is the _key_ (or _name_) of the provided data. This @@ -97,7 +96,6 @@ class ChildComponent(Component): def get_template_data(self, args, kwargs, slots, context): my_data = self.inject("invalid_key", DEFAULT_DATA) assert my_data == DEFAULT_DATA - return {} ``` !!! note diff --git a/docs/concepts/advanced/rendering_js_css.md b/docs/concepts/advanced/rendering_js_css.md index 6400163b..21621cca 100644 --- a/docs/concepts/advanced/rendering_js_css.md +++ b/docs/concepts/advanced/rendering_js_css.md @@ -109,8 +109,8 @@ fragment = MyComponent.render_to_response(deps_strategy="fragment") The `deps_strategy` parameter is set at the root of a component render tree, which is why it is not available for the [`{% component %}`](../../../reference/template_tags#component) tag. -When you use Django's [`django.shortcuts.render()`](https://docs.djangoproject.com/en/5.1/topics/http/shortcuts/#render) -or [`Template.render()`](https://docs.djangoproject.com/en/5.1/ref/templates/api/#django.template.Template.render) to render templates, +When you use Django's [`django.shortcuts.render()`](https://docs.djangoproject.com/en/5.2/topics/http/shortcuts/#render) +or [`Template.render()`](https://docs.djangoproject.com/en/5.2/ref/templates/api/#django.template.Template.render) to render templates, you can't directly set the `deps_strategy` parameter. In this case, you can set the `deps_strategy` with the `DJC_DEPS_STRATEGY` context variable. @@ -351,8 +351,8 @@ or templates can be rendered: - [`Component.render()`](../../../reference/api/#django_components.Component.render) - [`Component.render_to_response()`](../../../reference/api/#django_components.Component.render_to_response) -- [`Template.render()`](https://docs.djangoproject.com/en/5.1/ref/templates/api/#django.template.Template.render) -- [`django.shortcuts.render()`](https://docs.djangoproject.com/en/5.1/topics/http/shortcuts/#render) +- [`Template.render()`](https://docs.djangoproject.com/en/5.2/ref/templates/api/#django.template.Template.render) +- [`django.shortcuts.render()`](https://docs.djangoproject.com/en/5.2/topics/http/shortcuts/#render) This way you don't need to manually handle rendering of JS / CSS. diff --git a/docs/concepts/fundamentals/autodiscovery.md b/docs/concepts/fundamentals/autodiscovery.md index 5097a121..452db11a 100644 --- a/docs/concepts/fundamentals/autodiscovery.md +++ b/docs/concepts/fundamentals/autodiscovery.md @@ -48,7 +48,7 @@ By default, the Python files found in the [`COMPONENTS.app_dirs`](../../../reference/settings#django_components.app_settings.ComponentsSettings.app_dirs) are auto-imported in order to execute the code that registers the components. -Autodiscovery occurs when Django is loaded, during the [`AppConfig.ready()`](https://docs.djangoproject.com/en/5.1/ref/applications/#django.apps.AppConfig.ready) +Autodiscovery occurs when Django is loaded, during the [`AppConfig.ready()`](https://docs.djangoproject.com/en/5.2/ref/applications/#django.apps.AppConfig.ready) hook of the `apps.py` file. If you are using autodiscovery, keep a few points in mind: diff --git a/docs/concepts/fundamentals/component_views_urls.md b/docs/concepts/fundamentals/component_views_urls.md index 5166fda5..f77536b2 100644 --- a/docs/concepts/fundamentals/component_views_urls.md +++ b/docs/concepts/fundamentals/component_views_urls.md @@ -11,7 +11,7 @@ django-components has a suite of features that help you write and manage views a - For each component, you can define methods for handling HTTP requests (GET, POST, etc.) - `get()`, `post()`, etc. -- Use [`Component.as_view()`](../../../reference/api#django_components.Component.as_view) to be able to use your Components with Django's [`urlpatterns`](https://docs.djangoproject.com/en/5.1/topics/http/urls/). This works the same way as [`View.as_view()`](https://docs.djangoproject.com/en/5.1/ref/class-based-views/base/#django.views.generic.base.View.as_view). +- Use [`Component.as_view()`](../../../reference/api#django_components.Component.as_view) to be able to use your Components with Django's [`urlpatterns`](https://docs.djangoproject.com/en/5.2/topics/http/urls/). This works the same way as [`View.as_view()`](https://docs.djangoproject.com/en/5.2/ref/class-based-views/base/#django.views.generic.base.View.as_view). - To avoid having to manually define the endpoints for each component, you can set the component to be "public" with [`Component.View.public = True`](../../../reference/api#django_components.ComponentView.public). This will automatically create a URL for the component. To retrieve the component URL, use [`get_component_url()`](../../../reference/api#django_components.get_component_url). @@ -54,11 +54,11 @@ class Calendar(Component): !!! info - The View class supports all the same HTTP methods as Django's [`View`](https://docs.djangoproject.com/en/5.1/ref/class-based-views/base/#django.views.generic.base.View) class. These are: + The View class supports all the same HTTP methods as Django's [`View`](https://docs.djangoproject.com/en/5.2/ref/class-based-views/base/#django.views.generic.base.View) class. These are: `get()`, `post()`, `put()`, `patch()`, `delete()`, `head()`, `options()`, `trace()` - Each of these receive the [`HttpRequest`](https://docs.djangoproject.com/en/5.1/ref/request-response/#django.http.HttpRequest) object as the first argument. + Each of these receive the [`HttpRequest`](https://docs.djangoproject.com/en/5.2/ref/request-response/#django.http.HttpRequest) object as the first argument. @@ -108,7 +108,7 @@ class Calendar(Component): ## Register URLs manually To register the component as a route / endpoint in Django, add an entry to your -[`urlpatterns`](https://docs.djangoproject.com/en/5.1/topics/http/urls/). +[`urlpatterns`](https://docs.djangoproject.com/en/5.2/topics/http/urls/). In place of the view function, create a view object with [`Component.as_view()`](../../../reference/api#django_components.Component.as_view): ```python title="[project root]/urls.py" @@ -121,7 +121,7 @@ urlpatterns = [ ``` [`Component.as_view()`](../../../reference/api#django_components.Component.as_view) -internally calls [`View.as_view()`](https://docs.djangoproject.com/en/5.1/ref/class-based-views/base/#django.views.generic.base.View.as_view), passing the component +internally calls [`View.as_view()`](https://docs.djangoproject.com/en/5.2/ref/class-based-views/base/#django.views.generic.base.View.as_view), passing the component instance as one of the arguments. ## Register URLs automatically diff --git a/docs/concepts/fundamentals/http_request.md b/docs/concepts/fundamentals/http_request.md index c6d3847d..148fdc7b 100644 --- a/docs/concepts/fundamentals/http_request.md +++ b/docs/concepts/fundamentals/http_request.md @@ -56,7 +56,7 @@ class MyComponent(Component): ## Context Processors -Components support Django's [context processors](https://docs.djangoproject.com/en/5.1/ref/templates/api/#using-requestcontext). +Components support Django's [context processors](https://docs.djangoproject.com/en/5.2/ref/templates/api/#using-requestcontext). In regular Django templates, the context processors are applied only when the template is rendered with [`RequestContext`](https://docs.djangoproject.com/en/5.2/ref/templates/api/#django.template.RequestContext). diff --git a/docs/concepts/fundamentals/render_api.md b/docs/concepts/fundamentals/render_api.md index 09dfcff1..c3580692 100644 --- a/docs/concepts/fundamentals/render_api.md +++ b/docs/concepts/fundamentals/render_api.md @@ -80,8 +80,6 @@ class Table(Component): def get_template_data(self, args, kwargs, slots, context): # Access component's ID assert self.id == "c1A2b3c" - - return {} ``` ## Component inputs @@ -96,7 +94,7 @@ All the component inputs are captured and available as [`self.input`](../../../r - `context` - [`Context`](https://docs.djangoproject.com/en/5.2/ref/templates/api/#django.template.Context) object that should be used to render the component - And other kwargs passed to [`Component.render()`](../../../reference/api/#django_components.Component.render) like `type` and `render_dependencies` -Thus, use can use [`self.input.args`](../../../reference/api/#django_components.ComponentInput.args) +For example, you can use [`self.input.args`](../../../reference/api/#django_components.ComponentInput.args) and [`self.input.kwargs`](../../../reference/api/#django_components.ComponentInput.kwargs) to access the positional and keyword arguments passed to [`Component.render()`](../../../reference/api/#django_components.Component.render). @@ -109,8 +107,6 @@ class Table(Component): footer_slot = self.input.slots["footer"] some_var = self.input.context["some_var"] - return {} - rendered = TestComponent.render( kwargs={"variable": "test", "another": 1}, args=(123, "str"), @@ -120,9 +116,9 @@ rendered = TestComponent.render( ## Request object and context processors -If the component was either: +Components have access to the request object and context processors data if the component was: -- Given a [`request`](../../../reference/api/#django_components.Component.render) kwarg +- Given a [`request`](../../../reference/api/#django_components.Component.render) kwarg directly - Rendered with [`RenderContext`](https://docs.djangoproject.com/en/5.2/ref/templates/api/#django.template.RequestContext) - Nested in another component for which any of these conditions is true @@ -145,8 +141,6 @@ class Table(Component): assert self.request.GET == {"query": "something"} assert self.context_processors_data['user'].username == "admin" - return {} - rendered = Table.render( request=HttpRequest(), ) diff --git a/docs/concepts/fundamentals/rendering_components.md b/docs/concepts/fundamentals/rendering_components.md index 5f001280..d1f9639a 100644 --- a/docs/concepts/fundamentals/rendering_components.md +++ b/docs/concepts/fundamentals/rendering_components.md @@ -147,39 +147,39 @@ If you have embedded the component in a Django template using the You can simply render the template with the Django's API: -- [`django.shortcuts.render()`](https://docs.djangoproject.com/en/5.1/topics/http/shortcuts/#render) +- [`django.shortcuts.render()`](https://docs.djangoproject.com/en/5.2/topics/http/shortcuts/#render) - ```python - from django.shortcuts import render + ```python + from django.shortcuts import render - context = {"date": "2024-12-13"} - rendered_template = render(request, "my_template.html", context) - ``` + context = {"date": "2024-12-13"} + rendered_template = render(request, "my_template.html", context) + ``` -- [`Template.render()`](https://docs.djangoproject.com/en/5.1/ref/templates/api/#django.template.Template.render) +- [`Template.render()`](https://docs.djangoproject.com/en/5.2/ref/templates/api/#django.template.Template.render) - ```python - from django.template import Template - from django.template.loader import get_template + ```python + from django.template import Template + from django.template.loader import get_template - # Either from a file - template = get_template("my_template.html") + # Either from a file + template = get_template("my_template.html") - # or inlined - template = Template(""" - {% load component_tags %} -
- {% component "calendar" date="2024-12-13" / %} -
- """) + # or inlined + template = Template(""" + {% load component_tags %} +
+ {% component "calendar" date="2024-12-13" / %} +
+ """) - rendered_template = template.render() - ``` + rendered_template = template.render() + ``` ### Isolating components By default, components behave similarly to Django's -[`{% include %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#include), +[`{% include %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#include), and the template inside the component has access to the variables defined in the outer template. You can selectively isolate a component, using the `only` flag, so that the inner template @@ -244,8 +244,8 @@ Button.render( - `kwargs` - Keyword arguments to pass to the component (as a dictionary) - `slots` - Slot content to pass to the component (as a dictionary) - `context` - Django context for rendering (can be a dictionary or a `Context` object) -- `deps_strategy` - Dependencies rendering strategy (default: `"document"`) -- `request` - HTTP request object, used for context processors (optional) +- `deps_strategy` - [Dependencies rendering strategy](#dependencies-rendering) (default: `"document"`) +- `request` - [HTTP request object](../http_request), used for context processors (optional) - `escape_slots_content` - Whether to HTML-escape slot content (default: `True`) All arguments are optional. If not provided, they default to empty values or sensible defaults. @@ -416,7 +416,7 @@ Instead, use `args`, `kwargs`, and `slots` to pass data to the component. However, you can pass [`RequestContext`](https://docs.djangoproject.com/en/5.2/ref/templates/api/#django.template.RequestContext) to the `context` argument, so that the component will gain access to the request object and will use -[context processors](https://docs.djangoproject.com/en/5.1/ref/templates/api/#using-requestcontext). +[context processors](https://docs.djangoproject.com/en/5.2/ref/templates/api/#using-requestcontext). Read more on [Working with HTTP requests](../http_request). ```py diff --git a/docs/concepts/fundamentals/secondary_js_css_files.md b/docs/concepts/fundamentals/secondary_js_css_files.md index 478466fe..ad7e5873 100644 --- a/docs/concepts/fundamentals/secondary_js_css_files.md +++ b/docs/concepts/fundamentals/secondary_js_css_files.md @@ -28,11 +28,11 @@ class Calendar(Component): !!! note - django-component's management of files is inspired by [Django's `Media` class](https://docs.djangoproject.com/en/5.0/topics/forms/media/). + django-component's management of files is inspired by [Django's `Media` class](https://docs.djangoproject.com/en/5.2/topics/forms/media/). To be familiar with how Django handles static files, we recommend reading also: - - [How to manage static files (e.g. images, JavaScript, CSS)](https://docs.djangoproject.com/en/5.0/howto/static-files/) + - [How to manage static files (e.g. images, JavaScript, CSS)](https://docs.djangoproject.com/en/5.2/howto/static-files/) ## `Media` class @@ -50,14 +50,14 @@ class Calendar(Component): Use the `Media` class to define secondary JS / CSS files for a component. This `Media` class behaves similarly to -[Django's Media class](https://docs.djangoproject.com/en/5.1/topics/forms/media/#assets-as-a-static-definition): +[Django's Media class](https://docs.djangoproject.com/en/5.2/topics/forms/media/#assets-as-a-static-definition): - **Static paths** - Paths are handled as static file paths, and are resolved to URLs with Django's - [`{% static %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#static) template tag. -- **URLs** - A path that starts with `http`, `https`, or `/` is considered a URL. URLs are NOT resolved with [`{% static %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#static). + [`{% static %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#static) template tag. +- **URLs** - A path that starts with `http`, `https`, or `/` is considered a URL. URLs are NOT resolved with [`{% static %}`](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#static). - **HTML tags** - Both static paths and URLs are rendered to `', rendered) @@ -460,7 +460,7 @@ class TestMediaPathAsObject: Test that media work with paths defined as instances of classes that define the `__html__` method. - See https://docs.djangoproject.com/en/5.0/topics/forms/media/#paths-as-objects + See https://docs.djangoproject.com/en/5.2/topics/forms/media/#paths-as-objects """ # NOTE: @html_safe adds __html__ method from __str__ @@ -745,7 +745,7 @@ class TestMediaStaticfiles: # Configure static files. The dummy files are set up in the `./static_root` dir. # The URL should have path prefix /static/. # NOTE: We don't need STATICFILES_DIRS, because we don't run collectstatic - # See https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-STATICFILES_DIRS + # See https://docs.djangoproject.com/en/5.2/ref/settings/#std-setting-STATICFILES_DIRS "STATIC_URL": "static/", "STATIC_ROOT": os.path.join(Path(__file__).resolve().parent, "static_root"), # `django.contrib.staticfiles` MUST be installed for staticfiles resolution to work. @@ -793,11 +793,11 @@ class TestMediaStaticfiles: # Configure static files. The dummy files are set up in the `./static_root` dir. # The URL should have path prefix /static/. # NOTE: We don't need STATICFILES_DIRS, because we don't run collectstatic - # See https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-STATICFILES_DIRS + # See https://docs.djangoproject.com/en/5.2/ref/settings/#std-setting-STATICFILES_DIRS "STATIC_URL": "static/", "STATIC_ROOT": os.path.join(Path(__file__).resolve().parent, "static_root"), # NOTE: STATICFILES_STORAGE is deprecated since 5.1, use STORAGES instead - # See https://docs.djangoproject.com/en/5.0/ref/settings/#staticfiles-storage + # See https://docs.djangoproject.com/en/5.2/ref/settings/#storages "STORAGES": { # This was NOT changed "default": { diff --git a/tests/test_context.py b/tests/test_context.py index 17e0d579..64d4d037 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -561,7 +561,6 @@ class TestContextProcessors: nonlocal inner_request context_processors_data = self.context_processors_data inner_request = self.request - return {} template_str: types.django_html = """ {% load component_tags %} @@ -597,7 +596,6 @@ class TestContextProcessors: nonlocal parent_request context_processors_data = self.context_processors_data parent_request = self.request - return {} @register("test_child") class TestChildComponent(Component): @@ -608,7 +606,6 @@ class TestContextProcessors: nonlocal child_request context_processors_data_child = self.context_processors_data child_request = self.request - return {} template_str: types.django_html = """ {% load component_tags %} @@ -645,7 +642,6 @@ class TestContextProcessors: nonlocal parent_request context_processors_data = self.context_processors_data parent_request = self.request - return {} @register("test_child") class TestChildComponent(Component): @@ -656,7 +652,6 @@ class TestContextProcessors: nonlocal child_request context_processors_data_child = self.context_processors_data child_request = self.request - return {} template_str: types.django_html = """ {% load component_tags %} @@ -690,7 +685,6 @@ class TestContextProcessors: nonlocal inner_request context_processors_data = self.context_processors_data inner_request = self.request - return {} request = HttpRequest() request_context = RequestContext(request) @@ -719,7 +713,6 @@ class TestContextProcessors: nonlocal parent_request context_processors_data = self.context_processors_data parent_request = self.request - return {} @register("test_child") class TestChildComponent(Component): @@ -730,7 +723,6 @@ class TestContextProcessors: nonlocal child_request context_processors_data_child = self.context_processors_data child_request = self.request - return {} request = HttpRequest() request_context = RequestContext(request) @@ -756,7 +748,6 @@ class TestContextProcessors: nonlocal inner_request context_processors_data = self.context_processors_data inner_request = self.request - return {} request = HttpRequest() rendered = TestComponent.render(request=request) @@ -784,7 +775,6 @@ class TestContextProcessors: nonlocal parent_request context_processors_data = self.context_processors_data parent_request = self.request - return {} @register("test_child") class TestChildComponent(Component): @@ -795,7 +785,6 @@ class TestContextProcessors: nonlocal child_request context_processors_data_child = self.context_processors_data child_request = self.request - return {} request = HttpRequest() rendered = TestParentComponent.render(request=request) @@ -821,7 +810,6 @@ class TestContextProcessors: nonlocal inner_request context_processors_data = self.context_processors_data inner_request = self.request - return {} rendered = TestComponent.render(context=Context()) @@ -844,7 +832,6 @@ class TestContextProcessors: nonlocal inner_request context_processors_data = self.context_processors_data inner_request = self.request - return {} rendered = TestComponent.render() @@ -867,7 +854,6 @@ class TestContextProcessors: nonlocal inner_request context_processors_data = self.context_processors_data inner_request = self.request - return {} request = HttpRequest() rendered = TestComponent.render(Context(), request=request) @@ -906,7 +892,6 @@ class TestContextProcessors: def get_template_data(self, args, kwargs, slots, context): nonlocal context_processors_data context_processors_data = self.context_processors_data - return {} @register("test_child") class TestChildComponent(Component): @@ -915,7 +900,6 @@ class TestContextProcessors: def get_template_data(self, args, kwargs, slots, context): nonlocal context_processors_data_child context_processors_data_child = self.context_processors_data - return {} request = HttpRequest() TestParentComponent.render(request=request) diff --git a/tests/test_dependencies.py b/tests/test_dependencies.py index 6e67657b..d0aba6fb 100644 --- a/tests/test_dependencies.py +++ b/tests/test_dependencies.py @@ -693,9 +693,6 @@ class TestDependenciesStrategySimple: console.log("Hello"); """ - def get_template_data(self, args, kwargs, slots, context): - return {} - class Media: css = ["style.css", "style2.css"] js = "script2.js" @@ -715,9 +712,6 @@ class TestDependenciesStrategySimple: console.log("xyz"); """ - def get_template_data(self, args, kwargs, slots, context): - return {} - class Media: css = "xyz1.css" js = "xyz1.js" @@ -853,9 +847,6 @@ class TestDependenciesStrategyPrepend: console.log("Hello"); """ - def get_template_data(self, args, kwargs, slots, context): - return {} - class Media: css = ["style.css", "style2.css"] js = "script2.js" @@ -875,9 +866,6 @@ class TestDependenciesStrategyPrepend: console.log("xyz"); """ - def get_template_data(self, args, kwargs, slots, context): - return {} - class Media: css = "xyz1.css" js = "xyz1.js" @@ -1010,9 +998,6 @@ class TestDependenciesStrategyAppend: console.log("Hello"); """ - def get_template_data(self, args, kwargs, slots, context): - return {} - class Media: css = ["style.css", "style2.css"] js = "script2.js" @@ -1032,9 +1017,6 @@ class TestDependenciesStrategyAppend: console.log("xyz"); """ - def get_template_data(self, args, kwargs, slots, context): - return {} - class Media: css = "xyz1.css" js = "xyz1.js" diff --git a/tests/test_dependency_rendering.py b/tests/test_dependency_rendering.py index 519ecee0..336d6fd9 100644 --- a/tests/test_dependency_rendering.py +++ b/tests/test_dependency_rendering.py @@ -55,9 +55,6 @@ class SimpleComponentNested(Component): console.log("Hello"); """ - def get_template_data(self, args, kwargs, slots, context): - return {} - class Media: css = ["style.css", "style2.css"] js = "script2.js" @@ -78,9 +75,6 @@ class OtherComponent(Component): console.log("xyz"); """ - def get_template_data(self, args, kwargs, slots, context): - return {} - class Media: css = "xyz1.css" js = "xyz1.js" @@ -91,9 +85,6 @@ class SimpleComponentWithSharedDependency(Component): Variable: {{ variable }} """ - def get_template_data(self, args, kwargs, slots, context): - return {} - class Media: css = ["style.css", "style2.css"] js = ["script.js", "script2.js"] diff --git a/tests/test_expression.py b/tests/test_expression.py index 6fccb1a8..8694043e 100644 --- a/tests/test_expression.py +++ b/tests/test_expression.py @@ -751,7 +751,6 @@ class TestSpreadOperator: def get_template_data(self, args, kwargs, slots, context): nonlocal captured captured = args, kwargs - return {} template_str: types.django_html = ( """ @@ -815,7 +814,6 @@ class TestAggregateKwargs: def get_template_data(self, args, kwargs, slots, context): nonlocal captured captured = args, kwargs - return {} template_str: types.django_html = """ {% load component_tags %} diff --git a/tests/test_tag_parser.py b/tests/test_tag_parser.py index ef96438e..859715d3 100644 --- a/tests/test_tag_parser.py +++ b/tests/test_tag_parser.py @@ -2709,7 +2709,6 @@ class TestResolver: def get_template_data(self, args, kwargs, slots, context): nonlocal captured captured = kwargs - return {} template_str: types.django_html = """ {% load component_tags %} @@ -2774,7 +2773,6 @@ class TestResolver: def get_template_data(self, args, kwargs, slots, context): nonlocal captured captured = args, kwargs - return {} template_str: types.django_html = """ {% load component_tags %} @@ -2795,7 +2793,6 @@ class TestResolver: def get_template_data(self, args, kwargs, slots, context): nonlocal captured captured = args, kwargs - return {} template_str: types.django_html = """ {% load component_tags %} diff --git a/tests/test_templatetags_provide.py b/tests/test_templatetags_provide.py index ae588138..17b9fda9 100644 --- a/tests/test_templatetags_provide.py +++ b/tests/test_templatetags_provide.py @@ -213,9 +213,6 @@ class TestProvideTemplateTag:
""" - def get_template_data(self, args, kwargs, slots, context): - return {} - template_str: types.django_html = """ {% load component_tags %} {% provide "my_provide" key="hi" another=6 %} diff --git a/tests/test_templatetags_slot_fill.py b/tests/test_templatetags_slot_fill.py index 6e083227..59b94250 100644 --- a/tests/test_templatetags_slot_fill.py +++ b/tests/test_templatetags_slot_fill.py @@ -2411,7 +2411,6 @@ class TestSlotInput: def get_template_data(self, args, kwargs, slots, context): nonlocal seen_slots seen_slots = slots - return {} assert seen_slots == {} diff --git a/tox.ini b/tox.ini index c273d03c..61401839 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ # This library strives to support all officially supported combinations of Python and Django: -# https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django +# https://docs.djangoproject.com/en/5.2/faq/install/#what-python-version-can-i-use-with-django # https://devguide.python.org/versions/#versions [tox]