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 %} -