1
1
from pathlib import Path
2
2
3
+ import django
3
4
from django import forms
4
- from django .forms import Form
5
+ from django .forms import Form , BoundField as DjangoBoundField
5
6
from django .forms .renderers import DjangoTemplates , get_default_renderer
6
7
from django .utils .functional import cached_property
7
8
9
+
8
10
from dsfr .utils import dsfr_input_class_attr
9
11
10
12
@@ -24,17 +26,44 @@ def engine(self):
24
26
)
25
27
26
28
29
+ class BoundField (DjangoBoundField ):
30
+ @property
31
+ def template_name (self ):
32
+ template_name = self .field .template_name or getattr (
33
+ self .field .__class__ , "template_name" , None
34
+ )
35
+ if template_name :
36
+ return template_name
37
+
38
+ match self .widget_type :
39
+ case "checkboxinput" :
40
+ return "dsfr/form_field_snippets/checkbox_snippet.html"
41
+ case "checkboxselectmultiple" | "inlinecheckboxselectmultiple" :
42
+ return "dsfr/form_field_snippets/checkboxselectmultiple_snippet.html"
43
+ case "radioselect" | "inlineradioselect" :
44
+ return "dsfr/form_field_snippets/radioselect_snippet.html"
45
+ case "richradioselect" :
46
+ return "dsfr/form_field_snippets/richradioselect_snippet.html"
47
+ case "numbercursor" :
48
+ return "dsfr/form_field_snippets/numbercursor_snippet.html"
49
+ case "segmentedcontrol" :
50
+ return "dsfr/form_field_snippets/segmented_control_snippet.html"
51
+ case _:
52
+ return "dsfr/form_field_snippets/input_snippet.html"
53
+
54
+ def label_tag (self , contents = None , attrs = None , label_suffix = None , tag = None ):
55
+ if hasattr (self .field .widget , "dsfr_label_attrs" ):
56
+ attrs = {** self .field .widget .dsfr_label_attrs , ** (attrs or {})}
57
+ return super ().label_tag (contents , attrs , label_suffix , tag )
58
+
59
+
27
60
class DsfrBaseForm (Form ):
28
61
"""
29
62
A base form that adds the necessary classes on relevant fields
30
63
"""
31
64
32
65
template_name = "dsfr/form_snippet.html" # type: ignore
33
-
34
- def __init__ (self , * args , ** kwargs ):
35
- super ().__init__ (* args , ** kwargs )
36
- for visible in self .visible_fields ():
37
- dsfr_input_class_attr (visible )
66
+ bound_field_class = BoundField
38
67
39
68
@property
40
69
def default_renderer (self ):
@@ -46,6 +75,32 @@ def default_renderer(self):
46
75
else get_default_renderer ()
47
76
)
48
77
78
+ def __init__ (self , * args , ** kwargs ):
79
+ super ().__init__ (* args , ** kwargs )
80
+ for visible in self .visible_fields ():
81
+ dsfr_input_class_attr (visible )
82
+
83
+ if django .VERSION < (5 , 2 ):
84
+
85
+ def __getitem__ (self , name ):
86
+ try :
87
+ field = self .fields [name ]
88
+ except KeyError :
89
+ raise KeyError (
90
+ "Key '%s' not found in '%s'. Choices are: %s."
91
+ % (
92
+ name ,
93
+ self .__class__ .__name__ ,
94
+ ", " .join (sorted (self .fields )),
95
+ )
96
+ )
97
+ if name not in self ._bound_fields_cache :
98
+ bound_field_class = getattr (
99
+ self , "bound_field_class" , self .bound_field_class
100
+ )
101
+ self ._bound_fields_cache [name ] = bound_field_class (self , field , name )
102
+ return self ._bound_fields_cache [name ]
103
+
49
104
def set_autofocus_on_first_error (self ):
50
105
"""
51
106
Sets the autofocus on the first field with an error message.
0 commit comments