Skip to content

Instantly share code, notes, and snippets.

@nicoknoll
Last active July 30, 2019 16:15
Show Gist options
  • Save nicoknoll/6c9fd0cde260f0ce69918c9a1b1756dc to your computer and use it in GitHub Desktop.
Save nicoknoll/6c9fd0cde260f0ce69918c9a1b1756dc to your computer and use it in GitHub Desktop.
from django.template.loader import get_template
class ValidationError(Exception):
pass
class Field:
def __init__(self, default=None, required=False, allowed_type=None):
self.allowed_type = allowed_type
self.default = default
self.required = required
def clean(self, value=None):
default = self.default() if callable(self.default) else self.default
value = value or default
self.validate(value)
return value
def validate(self, value=None):
if self.allowed_type and not isinstance(value, self.allowed_type):
msg = '{} has to be of type {}.'
raise ValidationError(msg.format(value, self.allowed_type))
if self.required and value is None:
raise ValidationError('Field is required.')
class TemplateComponent:
template_name = None
def __init__(self, **data):
assert self.template_name is not None
self.fields = self.get_fields()
self.context = {
k: field.clean(data.get(k)) for k, field in self.fields.items()
}
def get_fields(self):
return {
k: getattr(self, k) for k in dir(self)
if isinstance(getattr(self, k), Field)
}
def render_with_context(self, context):
template = get_template(self.template_name)
return template.render(context=context)
def render(self):
return self.render_with_context(self.context)
def __str__(self):
return self.render()
class TextBlock(TemplateComponent):
"""
text_block.html':
<p>{{ text }}</p>
"""
template_name = 'text_block.html'
text = Field(required=True)
class BulletList(TemplateComponent):
"""
bullet_list.html':
<ul>
{% for li in list %}
<li>{{ li }}</li>
{% endif %}
</ul>
"""
template = 'bullet_list.html'
list = Field(required=True, default=list)
class TestEmail(TemplateComponent):
"""
emails/test-email.html:
{{ my_list }}
Bla
{{ my_text }}
"""
template_name = 'emails/test-email.html'
my_list = Field(required=True, allowed_type=BulletList)
my_text = Field(required=True, allowed_type=TextBlock)
"""
Useable Like:
context['test_email'] = TestEmail(
my_list=BulletList(list=['A', 'B']),
my_text=TextBlock(text='Bla Bla')
)
"""
@QuittyMR
Copy link

from abc import ABC
from dataclasses import dataclass, field
from typing import ClassVar, Any


@dataclass
class BaseClass(ABC):
    template_name: ClassVar[str]

    def __new__(cls, *args, **kwargs) -> Any:
        if not cls.template_name:
            raise AttributeError('attribute "template_name" is not defined on the class')
        return super().__new__(cls)


@dataclass
class ConcreteClass(BaseClass):
    template_name = ''
    a: str
    b: int


test = ConcreteClass('abc', 3)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment