Code

Ticket #8103: 8103.diff

File 8103.diff, 7.8 KB (added by SmileyChris, 6 years ago)
Line 
1Index: django/forms/widgets.py
2===================================================================
3--- django/forms/widgets.py     (revision 8204)
4+++ django/forms/widgets.py     (working copy)
5@@ -35,36 +35,36 @@
6             media_attrs = media.__dict__
7         else:
8             media_attrs = kwargs
9-           
10+
11         self._css = {}
12         self._js = []
13-       
14+
15         for name in MEDIA_TYPES:
16             getattr(self, 'add_' + name)(media_attrs.get(name, None))
17 
18         # Any leftover attributes must be invalid.
19         # if media_attrs != {}:
20         #     raise TypeError, "'class Media' has invalid attribute(s): %s" % ','.join(media_attrs.keys())
21-       
22+
23     def __unicode__(self):
24         return self.render()
25-       
26+
27     def render(self):
28         return u'\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES]))
29-       
30+
31     def render_js(self):
32         return [u'<script type="text/javascript" src="%s"></script>' % self.absolute_path(path) for path in self._js]
33-       
34+
35     def render_css(self):
36         # To keep rendering order consistent, we can't just iterate over items().
37         # We need to sort the keys, and iterate over the sorted list.
38         media = self._css.keys()
39         media.sort()
40         return chain(*[
41-            [u'<link href="%s" type="text/css" media="%s" rel="stylesheet" />' % (self.absolute_path(path), medium)
42-                    for path in self._css[medium]]
43+            [u'<link href="%s" type="text/css" media="%s" rel="stylesheet" />' % (self.absolute_path(path), medium)
44+                    for path in self._css[medium]]
45                 for medium in media])
46-       
47+
48     def absolute_path(self, path):
49         if path.startswith(u'http://') or path.startswith(u'https://') or path.startswith(u'/'):
50             return path
51@@ -77,9 +77,9 @@
52         raise KeyError('Unknown media type "%s"' % name)
53 
54     def add_js(self, data):
55-        if data:   
56+        if data:
57             self._js.extend([path for path in data if path not in self._js])
58-           
59+
60     def add_css(self, data):
61         if data:
62             for medium, paths in data.items():
63@@ -99,8 +99,8 @@
64             base = super(cls, self).media
65         else:
66             base = Media()
67-       
68-        # Get the media definition for this class   
69+
70+        # Get the media definition for this class
71         definition = getattr(cls, 'Media', None)
72         if definition:
73             extend = getattr(definition, 'extend', True)
74@@ -117,16 +117,16 @@
75         else:
76             return base
77     return property(_media)
78-   
79+
80 class MediaDefiningClass(type):
81     "Metaclass for classes that can have media definitions"
82-    def __new__(cls, name, bases, attrs):           
83+    def __new__(cls, name, bases, attrs):
84         new_class = super(MediaDefiningClass, cls).__new__(cls, name, bases,
85                                                            attrs)
86         if 'media' not in attrs:
87             new_class.media = media_property(new_class)
88         return new_class
89-       
90+
91 class Widget(object):
92     __metaclass__ = MediaDefiningClass
93     is_hidden = False          # Determines whether this corresponds to an <input type="hidden">.
94@@ -264,7 +264,7 @@
95     def value_from_datadict(self, data, files, name):
96         "File widgets take data from FILES, not POST"
97         return files.get(name, None)
98-   
99+
100     def _has_changed(self, initial, data):
101         if data is None:
102             return False
103@@ -334,6 +334,8 @@
104         return bool(initial) != bool(data)
105 
106 class Select(Widget):
107+    allow_multiple_selected = False
108+
109     def __init__(self, attrs=None, choices=()):
110         super(Select, self).__init__(attrs)
111         # choices can be any iterable, but we may need to render this widget
112@@ -354,7 +356,14 @@
113     def render_options(self, choices, selected_choices):
114         def render_option(option_value, option_label):
115             option_value = force_unicode(option_value)
116-            selected_html = (option_value in selected_choices) and u' selected="selected"' or ''
117+            if not getattr(render_option, 'selected', None)\
118+                            and option_value in selected_choices:
119+                if not self.allow_multiple_selected:
120+                    # Only allow for a single selection.
121+                    render_option.selected = True
122+                selected_html = u' selected="selected"'
123+            else:
124+                selected_html = ''
125             return u'<option value="%s"%s>%s</option>' % (
126                 escape(option_value), selected_html,
127                 conditional_escape(force_unicode(option_label)))
128@@ -396,6 +405,8 @@
129         return bool(initial) != bool(data)
130 
131 class SelectMultiple(Select):
132+    allow_multiple_selected = True
133+
134     def render(self, name, value, attrs=None, choices=()):
135         if value is None: value = []
136         final_attrs = self.build_attrs(attrs, name=name)
137@@ -410,7 +421,7 @@
138         if isinstance(data, MultiValueDict):
139             return data.getlist(name)
140         return data.get(name, None)
141-   
142+
143     def _has_changed(self, initial, data):
144         if initial is None:
145             initial = []
146@@ -527,7 +538,7 @@
147                 label_for = u' for="%s"' % final_attrs['id']
148             else:
149                 label_for = ''
150-               
151+
152             cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
153             option_value = force_unicode(option_value)
154             rendered_cb = cb.render(name, option_value)
155@@ -601,7 +612,7 @@
156 
157     def value_from_datadict(self, data, files, name):
158         return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
159-   
160+
161     def _has_changed(self, initial, data):
162         if initial is None:
163             initial = [u'' for x in range(0, len(data))]
164@@ -637,7 +648,7 @@
165             media = media + w.media
166         return media
167     media = property(_get_media)
168-   
169+
170 class SplitDateTimeWidget(MultiWidget):
171     """
172     A Widget that splits datetime input into two <input type="text"> boxes.
173Index: tests/regressiontests/forms/widgets.py
174===================================================================
175--- tests/regressiontests/forms/widgets.py      (revision 8204)
176+++ tests/regressiontests/forms/widgets.py      (working copy)
177@@ -458,6 +458,28 @@
178 <option value="4">4</option>
179 </select>
180 
181+Only one option can be selected:
182+>>> print w.render('choices', 0, choices=(('0', 'extra'),))
183+<select name="choices">
184+<option value="0" selected="selected">0</option>
185+<option value="1">1</option>
186+<option value="2">2</option>
187+<option value="3">3</option>
188+<option value="4">4</option>
189+<option value="0">extra</option>
190+</select>
191+
192+Ensure that it still selects the first element next time round:
193+>>> print w.render('choices', 0, choices=(('0', 'extra'),))
194+<select name="choices">
195+<option value="0" selected="selected">0</option>
196+<option value="1">1</option>
197+<option value="2">2</option>
198+<option value="3">3</option>
199+<option value="4">4</option>
200+<option value="0">extra</option>
201+</select>
202+
203 Choices can be nested one level in order to create HTML optgroups:
204 >>> w.choices=(('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))))
205 >>> print w.render('nestchoice', None)
206@@ -655,6 +677,15 @@
207 >>> w._has_changed([1, 2], [u'1', u'3'])
208 True
209 
210+Multiple options (with the same value) can be selected:
211+>>> print w.render('choices', [1], choices=(('1', 'extra'),))
212+<select multiple="multiple" name="choices">
213+<option value="1" selected="selected">1</option>
214+<option value="2">2</option>
215+<option value="3">3</option>
216+<option value="1" selected="selected">extra</option>
217+</select>
218+
219 # Choices can be nested one level in order to create HTML optgroups:
220 >>> w.choices = (('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))))
221 >>> print w.render('nestchoice', None)