mirror of
https://github.com/wrabit/django-cotton.git
synced 2025-08-04 15:18:20 +00:00
wip
This commit is contained in:
parent
553afeab44
commit
e83200401a
12 changed files with 116 additions and 12 deletions
|
@ -3,6 +3,8 @@
|
|||

|
||||

|
||||
|
||||
**Whilst we are still in 0.9.x versions, there could be breaking changes.**
|
||||
|
||||
Bringing component-based design to Django templates.
|
||||
|
||||
- <a href="https://django-cotton.com" target="_blank">Document site</a>
|
||||
|
|
|
@ -179,6 +179,11 @@ class Loader(BaseLoader):
|
|||
for var, value in c_vars.attrs.items():
|
||||
if value is None:
|
||||
vars_with_defaults.append(f"{var}={var}")
|
||||
elif var.startswith(":"):
|
||||
# If ':' is present, the user wants to parse a literal string as the default value,
|
||||
# i.e. "['a', 'b']", "{'a': 'b'}", "True", "False", "None" or "1".
|
||||
var = var[1:] # Remove the ':' prefix
|
||||
vars_with_defaults.append(f'{var}={var}|eval_default:"{value}"')
|
||||
else:
|
||||
# Assuming value is already a string that represents the default value
|
||||
vars_with_defaults.append(f'{var}={var}|default:"{value}"')
|
||||
|
@ -259,7 +264,7 @@ class CottonTemplateCacheHandler:
|
|||
"""Handles caching of cotton templates so the html parsing is only done on first load of each view or component."""
|
||||
|
||||
def __init__(self):
|
||||
self.enabled = getattr(settings, "TEMPLATE_CACHING_ENABLED", True)
|
||||
self.enabled = getattr(settings, "COTTON_TEMPLATE_CACHING_ENABLED", True)
|
||||
|
||||
def get_cache_key(self, template_name, mtime):
|
||||
template_hash = hashlib.sha256(template_name.encode()).hexdigest()
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
{% if none is None %}
|
||||
<p>none is None</p>
|
||||
{% endif %}
|
||||
|
||||
{% if number == 1 %}
|
||||
<p>number is 1</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_true is True %}
|
||||
<p>boolean_true is True</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_false is False %}
|
||||
<p>boolean_false is False</p>
|
||||
{% endif %}
|
||||
|
||||
{% if dict.key == 'value' %}
|
||||
<p>dict.key is 'value'</p>
|
||||
{% endif %}
|
||||
|
||||
{% if list.0 == 1 %}
|
||||
<p>list.0 is 1</p>
|
||||
{% endif %}
|
|
@ -0,0 +1,25 @@
|
|||
<c-vars :none="None" :number="1" :boolean_true="True" :boolean_false="False" :dict="{key: 'value'}" :list="[1, 2, 3]" />
|
||||
|
||||
{% if none is None %}
|
||||
<p>none is None</p>
|
||||
{% endif %}
|
||||
|
||||
{% if number == 1 %}
|
||||
<p>number is 1</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_true is True %}
|
||||
<p>boolean_true is True</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_false is False %}
|
||||
<p>boolean_false is False</p>
|
||||
{% endif %}
|
||||
|
||||
{% if dict.key == 'value' %}
|
||||
<p>dict.key is 'value'</p>
|
||||
{% endif %}
|
||||
|
||||
{% if list.0 == 1 %}
|
||||
<p>list.0 is 1</p>
|
||||
{% endif %}
|
|
@ -0,0 +1 @@
|
|||
<c-eval-attributes-test-component :none="None" :number="1" :boolean_true="True" :boolean_false="False" :dict="{key: 'value'}" :list="[1, 2, 3]" />
|
1
django_cotton/templates/eval_vars_test_view.cotton.html
Normal file
1
django_cotton/templates/eval_vars_test_view.cotton.html
Normal file
|
@ -0,0 +1 @@
|
|||
<c-eval-vars-test-component />
|
|
@ -6,10 +6,12 @@ register = template.Library()
|
|||
|
||||
|
||||
def cotton_vars_frame(parser, token):
|
||||
"""The job of the vars frame is to filter component kwargs (attributes) against declared vars. Because we
|
||||
desire to declare vars (<c-vars />) inside the same component that wants the vars in their context andbecause the
|
||||
component can not manipulate its own context from within it's own template, instead we wrap the vars frame around
|
||||
the contents of the component"""
|
||||
"""The job of the vars frame is:
|
||||
1. to filter out attributes declared as vars inside {{ attrs }} string.
|
||||
2. to provide default values to attributes. We want to be able to declare these in the same file as the component
|
||||
definition. Because we're effecting variables inside the same component, which is not possible usually, we we wrap
|
||||
the vars frame around the contents of the component so we can govern the attributes and vars that are available.
|
||||
"""
|
||||
bits = token.split_contents()[1:] # Skip the tag name
|
||||
|
||||
# Parse token kwargs while maintaining token order
|
||||
|
@ -27,21 +29,21 @@ class CottonVarsFrameNode(template.Node):
|
|||
|
||||
def render(self, context):
|
||||
# Assume 'attrs' are passed from the parent and are available in the context
|
||||
parent_attrs = context.get("attrs_dict", {})
|
||||
component_attrs = context.get("attrs_dict", {})
|
||||
|
||||
# Initialize vars based on the frame's kwargs and parent attrs
|
||||
vars = {}
|
||||
for key, value in self.kwargs.items():
|
||||
# Attempt to resolve each kwarg value (which may include template variables)
|
||||
resolved_value = value.resolve(context)
|
||||
# Check if the var exists in parent attrs; if so, use it, otherwise use the resolved default
|
||||
if key in parent_attrs:
|
||||
vars[key] = parent_attrs[key]
|
||||
# Check if the var exists in component attrs; if so, use it, otherwise use the resolved default
|
||||
if key in component_attrs:
|
||||
vars[key] = component_attrs[key]
|
||||
else:
|
||||
# Attempt to resolve each kwarg value (which may include template variables)
|
||||
resolved_value = value.resolve(context)
|
||||
vars[key] = resolved_value
|
||||
|
||||
# Overwrite 'attrs' in the local context by excluding keys that are identified as vars
|
||||
attrs_without_vars = {k: v for k, v in parent_attrs.items() if k not in vars}
|
||||
attrs_without_vars = {k: v for k, v in component_attrs.items() if k not in vars}
|
||||
context["attrs_dict"] = attrs_without_vars
|
||||
|
||||
# Provide all of the attrs as a string to pass to the component
|
||||
|
|
|
@ -4,6 +4,7 @@ from django.utils.html import format_html_join
|
|||
from django_cotton.templatetags._component import cotton_component
|
||||
from django_cotton.templatetags._slot import cotton_slot
|
||||
from django_cotton.templatetags._vars_frame import cotton_vars_frame
|
||||
from django_cotton.utils import eval_string
|
||||
|
||||
register = template.Library()
|
||||
register.tag("cotton_component", cotton_component)
|
||||
|
@ -22,3 +23,8 @@ def merge(attrs, args):
|
|||
else:
|
||||
attrs[key] = value
|
||||
return format_html_join(" ", '{0}="{1}"', attrs.items())
|
||||
|
||||
|
||||
@register.filter
|
||||
def eval_default(value, arg):
|
||||
return value or eval_string(arg)
|
||||
|
|
|
@ -102,3 +102,21 @@ class CottonTestCase(TestCase):
|
|||
response = self.client.get("/test/valueless-attributes")
|
||||
|
||||
self.assertContains(response, "It's True")
|
||||
|
||||
def test_component_attributes_can_converted_to_python_types(self):
|
||||
response = self.client.get("/test/eval-attributes")
|
||||
|
||||
self.assertContains(response, "none is None")
|
||||
self.assertContains(response, "number is 1")
|
||||
self.assertContains(response, "boolean_true is True")
|
||||
self.assertContains(response, "boolean_false is False")
|
||||
self.assertContains(response, "list.0 is 1")
|
||||
|
||||
def test_cvars_can_be_converted_to_python_types(self):
|
||||
response = self.client.get("/test/eval-vars")
|
||||
|
||||
self.assertContains(response, "none is None")
|
||||
self.assertContains(response, "number is 1")
|
||||
self.assertContains(response, "boolean_true is True")
|
||||
self.assertContains(response, "boolean_false is False")
|
||||
self.assertContains(response, "list.0 is 1")
|
||||
|
|
|
@ -43,4 +43,6 @@ urlpatterns = [
|
|||
),
|
||||
path("vars-test", TemplateView.as_view(template_name="vars_test.cotton.html")),
|
||||
path("variable-parsing", views.variable_parsing_test_view),
|
||||
path("test/eval-vars", views.eval_vars_test_view),
|
||||
path("test/eval-attributes", views.eval_attributes_test_view),
|
||||
]
|
||||
|
|
11
django_cotton/utils.py
Normal file
11
django_cotton/utils.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
import ast
|
||||
|
||||
|
||||
def eval_string(value):
|
||||
"""
|
||||
Evaluate a string representation of a constant, list, or dictionary to the actual Python object.
|
||||
"""
|
||||
try:
|
||||
return ast.literal_eval(value)
|
||||
except (ValueError, SyntaxError):
|
||||
return value
|
|
@ -42,3 +42,11 @@ def variable_parsing_test_view(request):
|
|||
|
||||
def valueless_attributes_test_view(request):
|
||||
return render(request, "valueless_attributes_test_view.cotton.html")
|
||||
|
||||
|
||||
def eval_vars_test_view(request):
|
||||
return render(request, "eval_vars_test_view.cotton.html")
|
||||
|
||||
|
||||
def eval_attributes_test_view(request):
|
||||
return render(request, "eval_attributes_test_view.cotton.html")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue