diff --git a/django_components/component.py b/django_components/component.py index da998144..834bdd94 100644 --- a/django_components/component.py +++ b/django_components/component.py @@ -1,4 +1,5 @@ import difflib +import inspect from collections import ChainMap from typing import Any, ClassVar, Dict, Iterable, Optional, Set, Tuple, Union @@ -87,10 +88,8 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass): self.outer_context: Context = outer_context or Context() self.fill_content = fill_content - @classmethod - @property - def class_hash(cls): - return hash(str(cls.__module__) + str(cls.__name__)) + def __init_subclass__(cls, **kwargs): + cls.class_hash = hash(inspect.getfile(cls) + cls.__name__) def get_context_data(self, *args, **kwargs) -> Dict[str, Any]: return {} diff --git a/django_components/template_loader.py b/django_components/template_loader.py index 6e3d564b..ac3372e0 100644 --- a/django_components/template_loader.py +++ b/django_components/template_loader.py @@ -12,17 +12,17 @@ from django.template.utils import get_app_template_dirs class Loader(FilesystemLoader): def get_dirs(self): component_dir = "components" - directories = list(get_app_template_dirs(component_dir)) + directories = set(get_app_template_dirs(component_dir)) if settings.SETTINGS_MODULE: settings_path = Path(*settings.SETTINGS_MODULE.split(".")) path = (settings_path / ".." / component_dir).resolve() if path.is_dir(): - directories.append(path) + directories.add(path) path = (settings_path / ".." / ".." / component_dir).resolve() if path.is_dir(): - directories.append(path) + directories.add(path) - return directories + return list(directories) diff --git a/sampleproject/components/calendar/calendar.py b/sampleproject/components/calendar/calendar.py index b4900faa..b2913470 100644 --- a/sampleproject/components/calendar/calendar.py +++ b/sampleproject/components/calendar/calendar.py @@ -14,6 +14,12 @@ class Calendar(component.Component): "date": date, } + def get(self, request, *args, **kwargs): + context = { + "date": request.GET.get("date", ""), + } + return self.render_to_response(context) + class Media: css = "calendar/calendar.css" js = "calendar/calendar.js" diff --git a/sampleproject/components/urls.py b/sampleproject/components/urls.py index 3419bf3e..5c5881e8 100644 --- a/sampleproject/components/urls.py +++ b/sampleproject/components/urls.py @@ -1,6 +1,8 @@ +from components.calendar.calendar import Calendar +from components.greeting import Greeting from django.urls import path -from greeting import Greeting urlpatterns = [ path("greeting/", Greeting.as_view(), name="greeting"), + path("calendar/", Calendar.as_view(), name="calendar"), ] diff --git a/tests/components/__init__.py b/tests/components/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/components/multi_file/multi_file.html b/tests/components/multi_file/multi_file.html new file mode 100644 index 00000000..e2f403bd --- /dev/null +++ b/tests/components/multi_file/multi_file.html @@ -0,0 +1,5 @@ +
+ {% csrf_token %} + + +
diff --git a/tests/components/multi_file/multi_file.py b/tests/components/multi_file/multi_file.py new file mode 100644 index 00000000..4400dacf --- /dev/null +++ b/tests/components/multi_file/multi_file.py @@ -0,0 +1,20 @@ +from typing import Any, Dict + +from django.http import HttpResponse + +from django_components import component + + +@component.register("multi_file_component") +class MultFileComponent(component.Component): + template_name = "multi_file/multi_file.html" + + def post(self, request, *args, **kwargs) -> HttpResponse: + variable = request.POST.get("variable") + return self.render_to_response({"variable": variable}) + + def get(self, request, *args, **kwargs) -> HttpResponse: + return self.render_to_response({"variable": "GET"}) + + def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]: + return {"variable": variable} diff --git a/tests/components/single_file.py b/tests/components/single_file.py new file mode 100644 index 00000000..956e9a7a --- /dev/null +++ b/tests/components/single_file.py @@ -0,0 +1,26 @@ +from typing import Any, Dict + +from django.http import HttpResponse + +from django_components import component + + +@component.register("single_file_component") +class SingleFileComponent(component.Component): + template = """ +
+ {% csrf_token %} + + +
+ """ + + def post(self, request, *args, **kwargs) -> HttpResponse: + variable = request.POST.get("variable") + return self.render_to_response({"variable": variable}) + + def get(self, request, *args, **kwargs) -> HttpResponse: + return self.render_to_response({"variable": "GET"}) + + def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]: + return {"variable": variable} diff --git a/tests/components/urls.py b/tests/components/urls.py new file mode 100644 index 00000000..fa1c0b61 --- /dev/null +++ b/tests/components/urls.py @@ -0,0 +1,9 @@ +from django.urls import path + +from tests.components.multi_file.multi_file import MultFileComponent +from tests.components.single_file import SingleFileComponent + +urlpatterns = [ + path("single/", SingleFileComponent.as_view(), name="single"), + path("multi/", MultFileComponent.as_view(), name="multi"), +] diff --git a/tests/test_autodiscover.py b/tests/test_autodiscover.py new file mode 100644 index 00000000..42a48633 --- /dev/null +++ b/tests/test_autodiscover.py @@ -0,0 +1,30 @@ +from django.urls import include, path + +# isort: off +from .django_test_setup import * # noqa +from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase + +# isort: on + + +from django_components import autodiscover, component + +urlpatterns = [ + path("", include("tests.components.urls")), +] + + +class TestAutodiscover(SimpleTestCase): + def setUp(self): + settings.SETTINGS_MODULE = "tests.test_autodiscover" # noqa + + def tearDown(self) -> None: + del settings.SETTINGS_MODULE # noqa + + def test_autodiscover_with_components_as_views(self): + try: + autodiscover() + except component.AlreadyRegistered: + self.fail( + "Autodiscover should not raise AlreadyRegistered exception" + )