refactor: fix - allow components with Url.public to be defined before django.setup() (#1112)

This commit is contained in:
Juro Oravec 2025-04-09 18:31:07 +02:00 committed by GitHub
parent cc249022c4
commit 07f747d705
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 47 additions and 22 deletions

View file

@ -1,5 +1,11 @@
# Release notes
## v0.138
#### Fix
- Fix bug: Allow components with `Url.public = True` to be defined before `django.setup()`
## v0.137
#### Feat

View file

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "django_components"
version = "0.137"
version = "0.138"
requires-python = ">=3.8, <4.0"
description = "A way to create simple reusable template components in Django."
keywords = ["django", "components", "css", "js", "html"]

View file

@ -658,27 +658,6 @@ class ExtensionManager:
self._initialized = True
# The triggers for following hooks may occur before the `apps.py` `ready()` hook is called.
# - on_component_class_created
# - on_component_class_deleted
# - on_registry_created
# - on_registry_deleted
# - on_component_registered
# - on_component_unregistered
#
# The problem is that the extensions are set up only at the initialization (`ready()` hook in `apps.py`).
#
# So in the case that these hooks are triggered before initialization,
# we store these "events" in a list, and then "flush" them all when `ready()` is called.
#
# This way, we can ensure that all extensions are present before any hooks are called.
for hook, data in self._events:
if hook == "on_component_class_created":
on_component_created_data: OnComponentClassCreatedContext = data
self._init_component_class(on_component_created_data.component_cls)
getattr(self, hook)(data)
self._events = []
# Populate the `urlpatterns` with URLs specified by the extensions
# TODO_V3 - Django-specific logic - replace with hook
urls: List[URLResolver] = []
@ -707,6 +686,29 @@ class ExtensionManager:
resolver = get_resolver(urlconf)
resolver._populate()
# Flush stored events
#
# The triggers for following hooks may occur before the `apps.py` `ready()` hook is called.
# - on_component_class_created
# - on_component_class_deleted
# - on_registry_created
# - on_registry_deleted
# - on_component_registered
# - on_component_unregistered
#
# The problem is that the extensions are set up only at the initialization (`ready()` hook in `apps.py`).
#
# So in the case that these hooks are triggered before initialization,
# we store these "events" in a list, and then "flush" them all when `ready()` is called.
#
# This way, we can ensure that all extensions are present before any hooks are called.
for hook, data in self._events:
if hook == "on_component_class_created":
on_component_created_data: OnComponentClassCreatedContext = data
self._init_component_class(on_component_created_data.component_cls)
getattr(self, hook)(data)
self._events = []
def get_extension(self, name: str) -> ComponentExtension:
for extension in self.extensions:
if extension.name == name:

View file

@ -9,6 +9,23 @@ from django_components.testing import djc_test
from .testutils import setup_test_config
# DO NOT REMOVE!
#
# This is intentionally defined before `setup_test_config()` in order to test that
# the URL extension works even before the Django has been set up.
#
# Because if we define the component before `django.setup()`, then we store it in
# event queue, and will register it when `AppConfig.ready()` is finally called.
#
# This test relies on the "url" extension calling `add_extension_urls()` from within
# the `on_component_class_created()` hook.
class ComponentBeforeReady(Component):
class Url:
public = True
template = "Hello"
setup_test_config({"autodiscover": False})