mirror of
https://github.com/django-components/django-components.git
synced 2025-07-07 17:34:59 +00:00
feat: expose _class_hash as class_id (#1094)
* feat: expose _class_hash as class_id * refactor: fix linting
This commit is contained in:
parent
a49f5e51dd
commit
bb5de86b69
13 changed files with 141 additions and 82 deletions
|
@ -4,6 +4,12 @@
|
|||
|
||||
#### Feat
|
||||
|
||||
- Each Component class now has a `class_id` attribute, which is unique to the component subclass.
|
||||
|
||||
NOTE: This is different from `Component.id`, which is unique to each rendered instance.
|
||||
|
||||
To look up a component class by its `class_id`, use `get_component_by_class_id()`.
|
||||
|
||||
- It's now easier to create URLs for component views.
|
||||
|
||||
Before, you had to call `Component.as_view()` and pass that to `urlpatterns`.
|
||||
|
|
|
@ -207,11 +207,11 @@ This is how we achieve that:
|
|||
|
||||
5. To be able to fetch component's inlined JS and CSS, django-components adds a URL path under:
|
||||
|
||||
`/components/cache/<str:comp_cls_hash>.<str:script_type>/`
|
||||
`/components/cache/<str:comp_cls_id>.<str:script_type>/`
|
||||
|
||||
E.g. `/components/cache/my_table_10bc2c.js/`
|
||||
E.g. `/components/cache/MyTable_10bc2c.js/`
|
||||
|
||||
This endpoint takes the component's unique hash, e.g. `my_table_10bc2c`, and looks up the component's inlined JS or CSS.
|
||||
This endpoint takes the component's unique ID, e.g. `MyTable_10bc2c`, and looks up the component's inlined JS or CSS.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -119,6 +119,10 @@
|
|||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.get_component_by_class_id
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.get_component_dirs
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
|
|
@ -22,6 +22,6 @@ urlpatterns = [
|
|||
## List of URLs
|
||||
|
||||
|
||||
- `components/cache/<str:comp_cls_hash>.<str:input_hash>.<str:script_type>`
|
||||
- `components/cache/<str:comp_cls_id>.<str:input_hash>.<str:script_type>`
|
||||
|
||||
- `components/cache/<str:comp_cls_hash>.<str:script_type>`
|
||||
- `components/cache/<str:comp_cls_id>.<str:script_type>`
|
||||
|
|
|
@ -201,6 +201,12 @@ def gen_reference_components():
|
|||
# If the component classes define any extra methods, we want to show them.
|
||||
# BUT, we don't to show the methods that belong to the base Component class.
|
||||
unique_methods = _get_unique_methods(Component, obj)
|
||||
|
||||
# NOTE: `class_id` is declared on the `Component` class, only as a type,
|
||||
# so it's not picked up by `_get_unique_methods`.
|
||||
if "class_id" in unique_methods:
|
||||
unique_methods.remove("class_id")
|
||||
|
||||
if unique_methods:
|
||||
members = ", ".join(unique_methods)
|
||||
members = f"[{unique_methods}]"
|
||||
|
@ -456,7 +462,7 @@ def gen_reference_urls():
|
|||
f.write(preface + "\n\n")
|
||||
|
||||
# Simply list all URLs, e.g.
|
||||
# `- components/cache/<str:comp_cls_hash>.<str:script_type>/`
|
||||
# `- components/cache/<str:comp_cls_id>.<str:script_type>/`
|
||||
f.write("\n".join([f"- `{url_path}`\n" for url_path in all_urls]))
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ from django_components.util.command import (
|
|||
CommandSubcommand,
|
||||
ComponentCommand,
|
||||
)
|
||||
from django_components.component import Component, ComponentVars, all_components
|
||||
from django_components.component import Component, ComponentVars, all_components, get_component_by_class_id
|
||||
from django_components.component_media import ComponentMediaInput, ComponentMediaInputPath
|
||||
from django_components.component_registry import (
|
||||
AlreadyRegistered,
|
||||
|
@ -96,6 +96,7 @@ __all__ = [
|
|||
"EmptyTuple",
|
||||
"EmptyDict",
|
||||
"format_attributes",
|
||||
"get_component_by_class_id",
|
||||
"get_component_dirs",
|
||||
"get_component_files",
|
||||
"get_component_url",
|
||||
|
|
|
@ -22,7 +22,7 @@ from typing import (
|
|||
Union,
|
||||
cast,
|
||||
)
|
||||
from weakref import ReferenceType, finalize
|
||||
from weakref import ReferenceType, WeakValueDictionary, finalize
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.forms.widgets import Media as MediaCls
|
||||
|
@ -46,7 +46,6 @@ from django_components.dependencies import (
|
|||
cache_component_css_vars,
|
||||
cache_component_js,
|
||||
cache_component_js_vars,
|
||||
comp_hash_mapping,
|
||||
insert_component_dependencies_comment,
|
||||
)
|
||||
from django_components.dependencies import render_dependencies as _render_dependencies
|
||||
|
@ -113,8 +112,10 @@ CssDataType = TypeVar("CssDataType", bound=Mapping[str, Any])
|
|||
# NOTE: `ReferenceType` is NOT a generic pre-3.9
|
||||
if sys.version_info >= (3, 9):
|
||||
AllComponents = List[ReferenceType[Type["Component"]]]
|
||||
CompHashMapping = WeakValueDictionary[str, Type["Component"]]
|
||||
else:
|
||||
AllComponents = List[ReferenceType]
|
||||
CompHashMapping = WeakValueDictionary
|
||||
|
||||
|
||||
# Keep track of all the Component classes created, so we can clean up after tests
|
||||
|
@ -131,6 +132,47 @@ def all_components() -> List[Type["Component"]]:
|
|||
return components
|
||||
|
||||
|
||||
# NOTE: Initially, we fetched components by their registered name, but that didn't work
|
||||
# for multiple registries and unregistered components.
|
||||
#
|
||||
# To have unique identifiers that works across registries, we rely
|
||||
# on component class' module import path (e.g. `path.to.my.MyComponent`).
|
||||
#
|
||||
# But we also don't want to expose the module import paths to the outside world, as
|
||||
# that information could be potentially exploited. So, instead, each component is
|
||||
# associated with a hash that's derived from its module import path, ensuring uniqueness,
|
||||
# consistency and privacy.
|
||||
#
|
||||
# E.g. `path.to.my.secret.MyComponent` -> `ab01f32`
|
||||
#
|
||||
# For easier debugging, we then prepend the hash with the component class name, so that
|
||||
# we can easily identify the component class by its hash.
|
||||
#
|
||||
# E.g. `path.to.my.secret.MyComponent` -> `MyComponent_ab01f32`
|
||||
#
|
||||
# The associations are defined as WeakValue map, so deleted components can be garbage
|
||||
# collected and automatically deleted from the dict.
|
||||
comp_cls_id_mapping: CompHashMapping = WeakValueDictionary()
|
||||
|
||||
|
||||
def get_component_by_class_id(comp_cls_id: str) -> Type["Component"]:
|
||||
"""
|
||||
Get a component class by its unique ID.
|
||||
|
||||
Each component class is associated with a unique hash that's derived from its module import path.
|
||||
|
||||
E.g. `path.to.my.secret.MyComponent` -> `MyComponent_ab01f32`
|
||||
|
||||
This hash is available under [`class_id`](../api#django_components.Component.class_id)
|
||||
on the component class.
|
||||
|
||||
Raises `KeyError` if the component class is not found.
|
||||
|
||||
NOTE: This is mainly intended for extensions.
|
||||
"""
|
||||
return comp_cls_id_mapping[comp_cls_id]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RenderInput(Generic[ArgsType, KwargsType, SlotsType]):
|
||||
context: Context
|
||||
|
@ -589,7 +631,18 @@ class Component(
|
|||
# MISC
|
||||
# #####################################
|
||||
|
||||
_class_hash: ClassVar[str]
|
||||
class_id: ClassVar[str]
|
||||
"""
|
||||
Unique ID of the component class, e.g. `MyComponent_ab01f2`.
|
||||
|
||||
This is derived from the component class' module import path, e.g. `path.to.my.MyComponent`.
|
||||
"""
|
||||
|
||||
# TODO_V1 - Remove this in v1
|
||||
@property
|
||||
def _class_hash(self) -> str:
|
||||
"""Deprecated. Use `Component.class_id` instead."""
|
||||
return self.class_id
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -622,8 +675,8 @@ class Component(
|
|||
extensions._init_component_instance(self)
|
||||
|
||||
def __init_subclass__(cls, **kwargs: Any) -> None:
|
||||
cls._class_hash = hash_comp_cls(cls)
|
||||
comp_hash_mapping[cls._class_hash] = cls
|
||||
cls.class_id = hash_comp_cls(cls)
|
||||
comp_cls_id_mapping[cls.class_id] = cls
|
||||
|
||||
ALL_COMPONENTS.append(cached_ref(cls)) # type: ignore[arg-type]
|
||||
extensions._init_component_class(cls)
|
||||
|
|
|
@ -351,7 +351,7 @@ class ComponentRegistry:
|
|||
```
|
||||
"""
|
||||
existing_component = self._registry.get(name)
|
||||
if existing_component and existing_component.cls._class_hash != component._class_hash:
|
||||
if existing_component and existing_component.cls.class_id != component.class_id:
|
||||
raise AlreadyRegistered('The component "%s" has already been registered' % name)
|
||||
|
||||
entry = self._register_to_library(name, component)
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import base64
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from hashlib import md5
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
|
@ -20,7 +19,6 @@ from typing import (
|
|||
Union,
|
||||
cast,
|
||||
)
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
from asgiref.sync import iscoroutinefunction, markcoroutinefunction
|
||||
from django.forms import Media
|
||||
|
@ -57,39 +55,18 @@ RenderType = Literal["document", "fragment"]
|
|||
#########################################################
|
||||
|
||||
|
||||
# NOTE: Initially, we fetched components by their registered name, but that didn't work
|
||||
# for multiple registries and unregistered components.
|
||||
#
|
||||
# To have unique identifiers that works across registries, we rely
|
||||
# on component class' module import path (e.g. `path.to.my.MyComponent`).
|
||||
#
|
||||
# But we also don't want to expose the module import paths to the outside world, as
|
||||
# that information could be potentially exploited. So, instead, each component is
|
||||
# associated with a hash that's derived from its module import path, ensuring uniqueness,
|
||||
# consistency and privacy.
|
||||
#
|
||||
# E.g. `path.to.my.secret.MyComponent` -> `MyComponent_ab01f32`
|
||||
#
|
||||
# The associations are defined as WeakValue map, so deleted components can be garbage
|
||||
# collected and automatically deleted from the dict.
|
||||
if sys.version_info < (3, 9):
|
||||
comp_hash_mapping: WeakValueDictionary = WeakValueDictionary()
|
||||
else:
|
||||
comp_hash_mapping: WeakValueDictionary[str, Type["Component"]] = WeakValueDictionary()
|
||||
|
||||
|
||||
# Generate keys like
|
||||
# `__components:MyButton_a78y37:js:df7c6d10`
|
||||
# `__components:MyButton_a78y37:css`
|
||||
def _gen_cache_key(
|
||||
comp_cls_hash: str,
|
||||
comp_cls_id: str,
|
||||
script_type: ScriptType,
|
||||
input_hash: Optional[str],
|
||||
) -> str:
|
||||
if input_hash:
|
||||
return f"__components:{comp_cls_hash}:{script_type}:{input_hash}"
|
||||
return f"__components:{comp_cls_id}:{script_type}:{input_hash}"
|
||||
else:
|
||||
return f"__components:{comp_cls_hash}:{script_type}"
|
||||
return f"__components:{comp_cls_id}:{script_type}"
|
||||
|
||||
|
||||
def _is_script_in_cache(
|
||||
|
@ -97,7 +74,7 @@ def _is_script_in_cache(
|
|||
script_type: ScriptType,
|
||||
input_hash: Optional[str],
|
||||
) -> bool:
|
||||
cache_key = _gen_cache_key(comp_cls._class_hash, script_type, input_hash)
|
||||
cache_key = _gen_cache_key(comp_cls.class_id, script_type, input_hash)
|
||||
cache = get_component_media_cache()
|
||||
return cache.has_key(cache_key)
|
||||
|
||||
|
@ -115,7 +92,7 @@ def _cache_script(
|
|||
|
||||
# E.g. `__components:MyButton:js:df7c6d10`
|
||||
if script_type in ("js", "css"):
|
||||
cache_key = _gen_cache_key(comp_cls._class_hash, script_type, input_hash)
|
||||
cache_key = _gen_cache_key(comp_cls.class_id, script_type, input_hash)
|
||||
else:
|
||||
raise ValueError(f"Unexpected script_type '{script_type}'")
|
||||
|
||||
|
@ -333,7 +310,7 @@ def insert_component_dependencies_comment(
|
|||
will be used by the ComponentDependencyMiddleware to collect all
|
||||
declared JS / CSS scripts.
|
||||
"""
|
||||
data = f"{component_cls._class_hash},{component_id},{js_input_hash or ''},{css_input_hash or ''}"
|
||||
data = f"{component_cls.class_id},{component_id},{js_input_hash or ''},{css_input_hash or ''}"
|
||||
|
||||
# NOTE: It's important that we put the comment BEFORE the content, so we can
|
||||
# use the order of comments to evaluate components' instance JS code in the correct order.
|
||||
|
@ -366,12 +343,12 @@ COMPONENT_DEPS_COMMENT = "<!-- _RENDERED {data} -->"
|
|||
# E.g. `<!-- _RENDERED table,123,a92ef298,bd002c3 -->`
|
||||
COMPONENT_COMMENT_REGEX = re.compile(rb"<!--\s+_RENDERED\s+(?P<data>[\w\-,/]+?)\s+-->")
|
||||
# E.g. `table,123,a92ef298,bd002c3`
|
||||
# - comp_cls_hash - Cache key of the component class that was rendered
|
||||
# - comp_cls_id - Cache key of the component class that was rendered
|
||||
# - id - Component render ID
|
||||
# - js - Cache key for the JS data from `get_js_data()`
|
||||
# - css - Cache key for the CSS data from `get_css_data()`
|
||||
SCRIPT_NAME_REGEX = re.compile(
|
||||
rb"^(?P<comp_cls_hash>[\w\-\./]+?),(?P<id>[\w]+?),(?P<js>[0-9a-f]*?),(?P<css>[0-9a-f]*?)$"
|
||||
rb"^(?P<comp_cls_id>[\w\-\./]+?),(?P<id>[\w]+?),(?P<js>[0-9a-f]*?),(?P<css>[0-9a-f]*?)$"
|
||||
)
|
||||
# E.g. `data-djc-id-a1b2c3`
|
||||
MAYBE_COMP_ID = r'(?: data-djc-id-\w{6}="")?'
|
||||
|
@ -550,26 +527,26 @@ def _process_dep_declarations(content: bytes, type: RenderType) -> Tuple[bytes,
|
|||
if not part_match:
|
||||
raise RuntimeError("Malformed dependencies data")
|
||||
|
||||
comp_cls_hash: str = part_match.group("comp_cls_hash").decode("utf-8")
|
||||
comp_cls_id: str = part_match.group("comp_cls_id").decode("utf-8")
|
||||
js_input_hash: Optional[str] = part_match.group("js").decode("utf-8") or None
|
||||
css_input_hash: Optional[str] = part_match.group("css").decode("utf-8") or None
|
||||
|
||||
if comp_cls_hash in seen_comp_hashes:
|
||||
if comp_cls_id in seen_comp_hashes:
|
||||
continue
|
||||
|
||||
comp_hashes.append(comp_cls_hash)
|
||||
seen_comp_hashes.add(comp_cls_hash)
|
||||
comp_hashes.append(comp_cls_id)
|
||||
seen_comp_hashes.add(comp_cls_id)
|
||||
|
||||
# Schedule to load the `<script>` / `<link>` tags for the JS / CSS from `Component.js/css`.
|
||||
comp_data.append((comp_cls_hash, "js", None))
|
||||
comp_data.append((comp_cls_hash, "css", None))
|
||||
comp_data.append((comp_cls_id, "js", None))
|
||||
comp_data.append((comp_cls_id, "css", None))
|
||||
|
||||
# Schedule to load the `<script>` / `<link>` tags for the JS / CSS variables.
|
||||
# Skip if no variables are defined.
|
||||
if js_input_hash is not None:
|
||||
inputs_data.append((comp_cls_hash, "js", js_input_hash))
|
||||
inputs_data.append((comp_cls_id, "js", js_input_hash))
|
||||
if css_input_hash is not None:
|
||||
inputs_data.append((comp_cls_hash, "css", css_input_hash))
|
||||
inputs_data.append((comp_cls_id, "css", css_input_hash))
|
||||
|
||||
(
|
||||
to_load_input_js_urls,
|
||||
|
@ -589,15 +566,17 @@ def _process_dep_declarations(content: bytes, type: RenderType) -> Tuple[bytes,
|
|||
loaded_component_css_urls,
|
||||
) = _prepare_tags_and_urls(comp_data, type)
|
||||
|
||||
def get_component_media(comp_cls_hash: str) -> Media:
|
||||
comp_cls = comp_hash_mapping[comp_cls_hash]
|
||||
def get_component_media(comp_cls_id: str) -> Media:
|
||||
from django_components.component import get_component_by_class_id
|
||||
|
||||
comp_cls = get_component_by_class_id(comp_cls_id)
|
||||
# NOTE: We instantiate the component classes so the `Media` are processed into `media`
|
||||
comp = comp_cls()
|
||||
return comp.media
|
||||
|
||||
all_medias = [
|
||||
# JS / CSS files from Component.Media.js/css.
|
||||
*[get_component_media(comp_cls_hash) for comp_cls_hash in comp_hashes],
|
||||
*[get_component_media(comp_cls_id) for comp_cls_id in comp_hashes],
|
||||
# All the inlined scripts that we plan to fetch / load
|
||||
Media(
|
||||
js=[*to_load_component_js_urls, *to_load_input_js_urls],
|
||||
|
@ -747,6 +726,8 @@ def _prepare_tags_and_urls(
|
|||
data: List[Tuple[str, ScriptType, Optional[str]]],
|
||||
type: RenderType,
|
||||
) -> Tuple[List[str], List[str], List[str], List[str], List[str], List[str]]:
|
||||
from django_components.component import get_component_by_class_id
|
||||
|
||||
to_load_js_urls: List[str] = []
|
||||
to_load_css_urls: List[str] = []
|
||||
inlined_js_tags: List[str] = []
|
||||
|
@ -758,12 +739,12 @@ def _prepare_tags_and_urls(
|
|||
# But even in that case we still need to call `Components.manager.markScriptLoaded`,
|
||||
# so the client knows NOT to fetch them again.
|
||||
# So in that case we populate both `inlined` and `loaded` lists
|
||||
for comp_cls_hash, script_type, input_hash in data:
|
||||
for comp_cls_id, script_type, input_hash in data:
|
||||
# NOTE: When CSS is scoped, then EVERY component instance will have different
|
||||
# copy of the style, because each copy will have component's ID embedded.
|
||||
# So, in that case we inline the style into the HTML (See `_link_dependencies_with_component_html`),
|
||||
# which means that we are NOT going to load / inline it again.
|
||||
comp_cls = comp_hash_mapping[comp_cls_hash]
|
||||
comp_cls = get_component_by_class_id(comp_cls_id)
|
||||
|
||||
if type == "document":
|
||||
# NOTE: Skip fetching of inlined JS/CSS if it's not defined or empty for given component
|
||||
|
@ -805,7 +786,7 @@ def get_script_content(
|
|||
input_hash: Optional[str],
|
||||
) -> Optional[str]:
|
||||
cache = get_component_media_cache()
|
||||
cache_key = _gen_cache_key(comp_cls._class_hash, script_type, input_hash)
|
||||
cache_key = _gen_cache_key(comp_cls.class_id, script_type, input_hash)
|
||||
script = cache.get(cache_key)
|
||||
|
||||
return script
|
||||
|
@ -819,8 +800,7 @@ def get_script_tag(
|
|||
content = get_script_content(script_type, comp_cls, input_hash)
|
||||
if content is None:
|
||||
raise RuntimeError(
|
||||
f"Could not find {script_type.upper()} for component '{comp_cls.__name__}' "
|
||||
f"(hash: {comp_cls._class_hash})"
|
||||
f"Could not find {script_type.upper()} for component '{comp_cls.__name__}' (id: {comp_cls.class_id})"
|
||||
)
|
||||
|
||||
if script_type == "js":
|
||||
|
@ -841,7 +821,7 @@ def get_script_url(
|
|||
return reverse(
|
||||
CACHE_ENDPOINT_NAME,
|
||||
kwargs={
|
||||
"comp_cls_hash": comp_cls._class_hash,
|
||||
"comp_cls_id": comp_cls.class_id,
|
||||
"script_type": script_type,
|
||||
**({"input_hash": input_hash} if input_hash is not None else {}),
|
||||
},
|
||||
|
@ -963,15 +943,18 @@ def _get_content_types(script_type: ScriptType) -> str:
|
|||
|
||||
def cached_script_view(
|
||||
req: HttpRequest,
|
||||
comp_cls_hash: str,
|
||||
comp_cls_id: str,
|
||||
script_type: ScriptType,
|
||||
input_hash: Optional[str] = None,
|
||||
) -> HttpResponse:
|
||||
from django_components.component import get_component_by_class_id
|
||||
|
||||
if req.method != "GET":
|
||||
return HttpResponseNotAllowed(["GET"])
|
||||
|
||||
comp_cls = comp_hash_mapping.get(comp_cls_hash)
|
||||
if comp_cls is None:
|
||||
try:
|
||||
comp_cls = get_component_by_class_id(comp_cls_id)
|
||||
except KeyError:
|
||||
return HttpResponseNotFound()
|
||||
|
||||
script = get_script_content(script_type, comp_cls, input_hash)
|
||||
|
@ -983,9 +966,9 @@ def cached_script_view(
|
|||
|
||||
|
||||
urlpatterns = [
|
||||
# E.g. `/components/cache/table.js` or `/components/cache/table.0ab2c3.js`
|
||||
path("cache/<str:comp_cls_hash>.<str:input_hash>.<str:script_type>", cached_script_view, name=CACHE_ENDPOINT_NAME),
|
||||
path("cache/<str:comp_cls_hash>.<str:script_type>", cached_script_view, name=CACHE_ENDPOINT_NAME),
|
||||
# E.g. `/components/cache/MyTable_a1b2c3.js` or `/components/cache/MyTable_a1b2c3.0ab2c3.js`
|
||||
path("cache/<str:comp_cls_id>.<str:input_hash>.<str:script_type>", cached_script_view, name=CACHE_ENDPOINT_NAME),
|
||||
path("cache/<str:comp_cls_id>.<str:script_type>", cached_script_view, name=CACHE_ENDPOINT_NAME),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ else:
|
|||
|
||||
|
||||
def _get_component_route_name(component: Union[Type["Component"], "Component"]) -> str:
|
||||
return f"__component_url__{component._class_hash}"
|
||||
return f"__component_url__{component.class_id}"
|
||||
|
||||
|
||||
def get_component_url(component: Union[Type["Component"], "Component"]) -> str:
|
||||
|
@ -162,7 +162,7 @@ class UrlExtension(ComponentExtension):
|
|||
# Create a URL route like `components/MyTable_a1b2c3/`
|
||||
# And since this is within the `url` extension, the full URL path will then be:
|
||||
# `/components/ext/url/components/MyTable_a1b2c3/`
|
||||
route_path = f"components/{ctx.component_cls._class_hash}/"
|
||||
route_path = f"components/{ctx.component_cls.class_id}/"
|
||||
route_name = _get_component_route_name(ctx.component_cls)
|
||||
route = URLRoute(
|
||||
path=route_path,
|
||||
|
|
|
@ -129,16 +129,16 @@ class TestComponentMediaCache:
|
|||
TestMediaAndVarsComponent.render()
|
||||
|
||||
# Check that JS/CSS is cached for components that have them
|
||||
assert test_cache.has_key(f"__components:{TestMediaAndVarsComponent._class_hash}:js")
|
||||
assert test_cache.has_key(f"__components:{TestMediaAndVarsComponent._class_hash}:css")
|
||||
assert test_cache.has_key(f"__components:{TestMediaNoVarsComponent._class_hash}:js")
|
||||
assert test_cache.has_key(f"__components:{TestMediaNoVarsComponent._class_hash}:css")
|
||||
assert not test_cache.has_key(f"__components:{TestSimpleComponent._class_hash}:js")
|
||||
assert not test_cache.has_key(f"__components:{TestSimpleComponent._class_hash}:css")
|
||||
assert test_cache.has_key(f"__components:{TestMediaAndVarsComponent.class_id}:js")
|
||||
assert test_cache.has_key(f"__components:{TestMediaAndVarsComponent.class_id}:css")
|
||||
assert test_cache.has_key(f"__components:{TestMediaNoVarsComponent.class_id}:js")
|
||||
assert test_cache.has_key(f"__components:{TestMediaNoVarsComponent.class_id}:css")
|
||||
assert not test_cache.has_key(f"__components:{TestSimpleComponent.class_id}:js")
|
||||
assert not test_cache.has_key(f"__components:{TestSimpleComponent.class_id}:css")
|
||||
|
||||
# Check that we cache `Component.js` / `Component.css`
|
||||
assert test_cache.get(f"__components:{TestMediaNoVarsComponent._class_hash}:js").strip() == "console.log('Hello from JS');" # noqa: E501
|
||||
assert test_cache.get(f"__components:{TestMediaNoVarsComponent._class_hash}:css").strip() == ".novars-component { color: blue; }" # noqa: E501
|
||||
assert test_cache.get(f"__components:{TestMediaNoVarsComponent.class_id}:js").strip() == "console.log('Hello from JS');" # noqa: E501
|
||||
assert test_cache.get(f"__components:{TestMediaNoVarsComponent.class_id}:css").strip() == ".novars-component { color: blue; }" # noqa: E501
|
||||
|
||||
# Check that we cache JS / CSS scripts generated from `get_js_data` / `get_css_data`
|
||||
# NOTE: The hashes is generated from the data.
|
||||
|
@ -146,5 +146,5 @@ class TestComponentMediaCache:
|
|||
css_vars_hash = "d039a3"
|
||||
|
||||
# TODO - Update once JS and CSS vars are enabled
|
||||
assert test_cache.get(f"__components:{TestMediaAndVarsComponent._class_hash}:js:{js_vars_hash}").strip() == ""
|
||||
assert test_cache.get(f"__components:{TestMediaAndVarsComponent._class_hash}:css:{css_vars_hash}").strip() == "" # noqa: E501
|
||||
assert test_cache.get(f"__components:{TestMediaAndVarsComponent.class_id}:js:{js_vars_hash}").strip() == ""
|
||||
assert test_cache.get(f"__components:{TestMediaAndVarsComponent.class_id}:css:{css_vars_hash}").strip() == "" # noqa: E501
|
||||
|
|
|
@ -16,7 +16,7 @@ from django.test import Client
|
|||
from django.urls import path
|
||||
from pytest_django.asserts import assertHTMLEqual, assertInHTML
|
||||
|
||||
from django_components import Component, ComponentView, all_components, register, types
|
||||
from django_components import Component, ComponentView, all_components, get_component_by_class_id, register, types
|
||||
from django_components.slots import SlotRef
|
||||
from django_components.urls import urlpatterns as dc_urlpatterns
|
||||
|
||||
|
@ -356,6 +356,12 @@ class TestComponent:
|
|||
):
|
||||
Root.render()
|
||||
|
||||
def test_get_component_by_id(self):
|
||||
class SimpleComponent(Component):
|
||||
pass
|
||||
|
||||
assert get_component_by_class_id(SimpleComponent.class_id) == SimpleComponent
|
||||
|
||||
|
||||
@djc_test
|
||||
class TestComponentRender:
|
||||
|
|
|
@ -41,7 +41,7 @@ class TestComponentUrl:
|
|||
|
||||
# Check if the URL is correctly generated
|
||||
component_url = get_component_url(TestComponent)
|
||||
assert component_url == f"/components/ext/url/components/{TestComponent._class_hash}/"
|
||||
assert component_url == f"/components/ext/url/components/{TestComponent.class_id}/"
|
||||
|
||||
client = Client()
|
||||
response = client.get(component_url)
|
||||
|
@ -79,7 +79,7 @@ class TestComponentUrl:
|
|||
get_component_url(TestComponent)
|
||||
|
||||
# Even calling the URL directly should raise an error
|
||||
component_url = f"/components/ext/url/components/{TestComponent._class_hash}/"
|
||||
component_url = f"/components/ext/url/components/{TestComponent.class_id}/"
|
||||
|
||||
client = Client()
|
||||
response = client.get(component_url)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue