Fixed #7195 -- Fixed the validation of MultipleChoice fields so that they can
be populated from request.REQUEST. Based on a patch from Daniel Roseman. git-svn-id: http://code.djangoproject.com/svn/django/trunk@8525 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
d6e5632969
commit
6d6fb392b4
|
@ -10,7 +10,7 @@ except NameError:
|
||||||
import copy
|
import copy
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.datastructures import MultiValueDict
|
from django.utils.datastructures import MultiValueDict, MergeDict
|
||||||
from django.utils.html import escape, conditional_escape
|
from django.utils.html import escape, conditional_escape
|
||||||
from django.utils.translation import ugettext
|
from django.utils.translation import ugettext
|
||||||
from django.utils.encoding import StrAndUnicode, force_unicode
|
from django.utils.encoding import StrAndUnicode, force_unicode
|
||||||
|
@ -35,36 +35,36 @@ class Media(StrAndUnicode):
|
||||||
media_attrs = media.__dict__
|
media_attrs = media.__dict__
|
||||||
else:
|
else:
|
||||||
media_attrs = kwargs
|
media_attrs = kwargs
|
||||||
|
|
||||||
self._css = {}
|
self._css = {}
|
||||||
self._js = []
|
self._js = []
|
||||||
|
|
||||||
for name in MEDIA_TYPES:
|
for name in MEDIA_TYPES:
|
||||||
getattr(self, 'add_' + name)(media_attrs.get(name, None))
|
getattr(self, 'add_' + name)(media_attrs.get(name, None))
|
||||||
|
|
||||||
# Any leftover attributes must be invalid.
|
# Any leftover attributes must be invalid.
|
||||||
# if media_attrs != {}:
|
# if media_attrs != {}:
|
||||||
# raise TypeError, "'class Media' has invalid attribute(s): %s" % ','.join(media_attrs.keys())
|
# raise TypeError, "'class Media' has invalid attribute(s): %s" % ','.join(media_attrs.keys())
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.render()
|
return self.render()
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
return mark_safe(u'\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES])))
|
return mark_safe(u'\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES])))
|
||||||
|
|
||||||
def render_js(self):
|
def render_js(self):
|
||||||
return [u'<script type="text/javascript" src="%s"></script>' % self.absolute_path(path) for path in self._js]
|
return [u'<script type="text/javascript" src="%s"></script>' % self.absolute_path(path) for path in self._js]
|
||||||
|
|
||||||
def render_css(self):
|
def render_css(self):
|
||||||
# To keep rendering order consistent, we can't just iterate over items().
|
# To keep rendering order consistent, we can't just iterate over items().
|
||||||
# We need to sort the keys, and iterate over the sorted list.
|
# We need to sort the keys, and iterate over the sorted list.
|
||||||
media = self._css.keys()
|
media = self._css.keys()
|
||||||
media.sort()
|
media.sort()
|
||||||
return chain(*[
|
return chain(*[
|
||||||
[u'<link href="%s" type="text/css" media="%s" rel="stylesheet" />' % (self.absolute_path(path), medium)
|
[u'<link href="%s" type="text/css" media="%s" rel="stylesheet" />' % (self.absolute_path(path), medium)
|
||||||
for path in self._css[medium]]
|
for path in self._css[medium]]
|
||||||
for medium in media])
|
for medium in media])
|
||||||
|
|
||||||
def absolute_path(self, path):
|
def absolute_path(self, path):
|
||||||
if path.startswith(u'http://') or path.startswith(u'https://') or path.startswith(u'/'):
|
if path.startswith(u'http://') or path.startswith(u'https://') or path.startswith(u'/'):
|
||||||
return path
|
return path
|
||||||
|
@ -77,9 +77,9 @@ class Media(StrAndUnicode):
|
||||||
raise KeyError('Unknown media type "%s"' % name)
|
raise KeyError('Unknown media type "%s"' % name)
|
||||||
|
|
||||||
def add_js(self, data):
|
def add_js(self, data):
|
||||||
if data:
|
if data:
|
||||||
self._js.extend([path for path in data if path not in self._js])
|
self._js.extend([path for path in data if path not in self._js])
|
||||||
|
|
||||||
def add_css(self, data):
|
def add_css(self, data):
|
||||||
if data:
|
if data:
|
||||||
for medium, paths in data.items():
|
for medium, paths in data.items():
|
||||||
|
@ -99,8 +99,8 @@ def media_property(cls):
|
||||||
base = super(cls, self).media
|
base = super(cls, self).media
|
||||||
else:
|
else:
|
||||||
base = Media()
|
base = Media()
|
||||||
|
|
||||||
# Get the media definition for this class
|
# Get the media definition for this class
|
||||||
definition = getattr(cls, 'Media', None)
|
definition = getattr(cls, 'Media', None)
|
||||||
if definition:
|
if definition:
|
||||||
extend = getattr(definition, 'extend', True)
|
extend = getattr(definition, 'extend', True)
|
||||||
|
@ -117,16 +117,16 @@ def media_property(cls):
|
||||||
else:
|
else:
|
||||||
return base
|
return base
|
||||||
return property(_media)
|
return property(_media)
|
||||||
|
|
||||||
class MediaDefiningClass(type):
|
class MediaDefiningClass(type):
|
||||||
"Metaclass for classes that can have media definitions"
|
"Metaclass for classes that can have media definitions"
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
new_class = super(MediaDefiningClass, cls).__new__(cls, name, bases,
|
new_class = super(MediaDefiningClass, cls).__new__(cls, name, bases,
|
||||||
attrs)
|
attrs)
|
||||||
if 'media' not in attrs:
|
if 'media' not in attrs:
|
||||||
new_class.media = media_property(new_class)
|
new_class.media = media_property(new_class)
|
||||||
return new_class
|
return new_class
|
||||||
|
|
||||||
class Widget(object):
|
class Widget(object):
|
||||||
__metaclass__ = MediaDefiningClass
|
__metaclass__ = MediaDefiningClass
|
||||||
is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
|
is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
|
||||||
|
@ -250,7 +250,7 @@ class MultipleHiddenInput(HiddenInput):
|
||||||
for v in value]))
|
for v in value]))
|
||||||
|
|
||||||
def value_from_datadict(self, data, files, name):
|
def value_from_datadict(self, data, files, name):
|
||||||
if isinstance(data, MultiValueDict):
|
if isinstance(data, (MultiValueDict, MergeDict)):
|
||||||
return data.getlist(name)
|
return data.getlist(name)
|
||||||
return data.get(name, None)
|
return data.get(name, None)
|
||||||
|
|
||||||
|
@ -264,7 +264,7 @@ class FileInput(Input):
|
||||||
def value_from_datadict(self, data, files, name):
|
def value_from_datadict(self, data, files, name):
|
||||||
"File widgets take data from FILES, not POST"
|
"File widgets take data from FILES, not POST"
|
||||||
return files.get(name, None)
|
return files.get(name, None)
|
||||||
|
|
||||||
def _has_changed(self, initial, data):
|
def _has_changed(self, initial, data):
|
||||||
if data is None:
|
if data is None:
|
||||||
return False
|
return False
|
||||||
|
@ -417,10 +417,10 @@ class SelectMultiple(Select):
|
||||||
return mark_safe(u'\n'.join(output))
|
return mark_safe(u'\n'.join(output))
|
||||||
|
|
||||||
def value_from_datadict(self, data, files, name):
|
def value_from_datadict(self, data, files, name):
|
||||||
if isinstance(data, MultiValueDict):
|
if isinstance(data, (MultiValueDict, MergeDict)):
|
||||||
return data.getlist(name)
|
return data.getlist(name)
|
||||||
return data.get(name, None)
|
return data.get(name, None)
|
||||||
|
|
||||||
def _has_changed(self, initial, data):
|
def _has_changed(self, initial, data):
|
||||||
if initial is None:
|
if initial is None:
|
||||||
initial = []
|
initial = []
|
||||||
|
@ -537,7 +537,7 @@ class CheckboxSelectMultiple(SelectMultiple):
|
||||||
label_for = u' for="%s"' % final_attrs['id']
|
label_for = u' for="%s"' % final_attrs['id']
|
||||||
else:
|
else:
|
||||||
label_for = ''
|
label_for = ''
|
||||||
|
|
||||||
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
|
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
|
||||||
option_value = force_unicode(option_value)
|
option_value = force_unicode(option_value)
|
||||||
rendered_cb = cb.render(name, option_value)
|
rendered_cb = cb.render(name, option_value)
|
||||||
|
@ -611,7 +611,7 @@ class MultiWidget(Widget):
|
||||||
|
|
||||||
def value_from_datadict(self, data, files, name):
|
def value_from_datadict(self, data, files, name):
|
||||||
return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
|
return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
|
||||||
|
|
||||||
def _has_changed(self, initial, data):
|
def _has_changed(self, initial, data):
|
||||||
if initial is None:
|
if initial is None:
|
||||||
initial = [u'' for x in range(0, len(data))]
|
initial = [u'' for x in range(0, len(data))]
|
||||||
|
@ -647,7 +647,7 @@ class MultiWidget(Widget):
|
||||||
media = media + w.media
|
media = media + w.media
|
||||||
return media
|
return media
|
||||||
media = property(_get_media)
|
media = property(_get_media)
|
||||||
|
|
||||||
class SplitDateTimeWidget(MultiWidget):
|
class SplitDateTimeWidget(MultiWidget):
|
||||||
"""
|
"""
|
||||||
A Widget that splits datetime input into two <input type="text"> boxes.
|
A Widget that splits datetime input into two <input type="text"> boxes.
|
||||||
|
|
|
@ -540,8 +540,9 @@ zero-based index.
|
||||||
<li><label for="composers_id_1"><input type="checkbox" name="composers" value="P" id="composers_id_1" /> Paul McCartney</label></li>
|
<li><label for="composers_id_1"><input type="checkbox" name="composers" value="P" id="composers_id_1" /> Paul McCartney</label></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
Data for a MultipleChoiceField should be a list. QueryDict and MultiValueDict
|
Data for a MultipleChoiceField should be a list. QueryDict, MultiValueDict and
|
||||||
conveniently work with this.
|
MergeDict (when created as a merge of MultiValueDicts) conveniently work with
|
||||||
|
this.
|
||||||
>>> data = {'name': 'Yesterday', 'composers': ['J', 'P']}
|
>>> data = {'name': 'Yesterday', 'composers': ['J', 'P']}
|
||||||
>>> f = SongForm(data)
|
>>> f = SongForm(data)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
|
@ -556,6 +557,11 @@ conveniently work with this.
|
||||||
>>> f = SongForm(data)
|
>>> f = SongForm(data)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
|
>>> from django.utils.datastructures import MergeDict
|
||||||
|
>>> data = MergeDict(MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])))
|
||||||
|
>>> f = SongForm(data)
|
||||||
|
>>> f.errors
|
||||||
|
{}
|
||||||
|
|
||||||
The MultipleHiddenInput widget renders multiple values as hidden fields.
|
The MultipleHiddenInput widget renders multiple values as hidden fields.
|
||||||
>>> class SongFormHidden(Form):
|
>>> class SongFormHidden(Form):
|
||||||
|
|
Loading…
Reference in New Issue