1import functools
2from pathlib import Path
3
4from django.conf import settings
5from django.template.backends.django import DjangoTemplates
6from django.template.loader import get_template
7from django.utils.functional import cached_property
8from django.utils.module_loading import import_string
9
10
11@functools.lru_cache
12def get_default_renderer():
13 renderer_class = import_string(settings.FORM_RENDERER)
14 return renderer_class()
15
16
17class BaseRenderer:
18 form_template_name = "django/forms/div.html"
19 formset_template_name = "django/forms/formsets/div.html"
20 field_template_name = "django/forms/field.html"
21
22 bound_field_class = None
23
24 def get_template(self, template_name):
25 raise NotImplementedError("subclasses must implement get_template()")
26
27 def render(self, template_name, context, request=None):
28 template = self.get_template(template_name)
29 return template.render(context, request=request).strip()
30
31
32class EngineMixin:
33 def get_template(self, template_name):
34 return self.engine.get_template(template_name)
35
36 @cached_property
37 def engine(self):
38 return self.backend(
39 {
40 "APP_DIRS": True,
41 "DIRS": [Path(__file__).parent / self.backend.app_dirname],
42 "NAME": "djangoforms",
43 "OPTIONS": {},
44 }
45 )
46
47
48class DjangoTemplates(EngineMixin, BaseRenderer):
49 """
50 Load Django templates from the built-in widget templates in
51 django/forms/templates and from apps' 'templates' directory.
52 """
53
54 backend = DjangoTemplates
55
56
57class Jinja2(EngineMixin, BaseRenderer):
58 """
59 Load Jinja2 templates from the built-in widget templates in
60 django/forms/jinja2 and from apps' 'jinja2' directory.
61 """
62
63 @cached_property
64 def backend(self):
65 from django.template.backends.jinja2 import Jinja2
66
67 return Jinja2
68
69
70class TemplatesSetting(BaseRenderer):
71 """
72 Load templates using template.loader.get_template() which is configured
73 based on settings.TEMPLATES.
74 """
75
76 def get_template(self, template_name):
77 return get_template(template_name)