newforms: Fixed #3008 -- Widgets now support strings containing utf-8 characters. Thanks for reporting, Nebojša Đorđević
git-svn-id: http://code.djangoproject.com/svn/django/trunk@4076 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
75516392b8
commit
6dd4e6d046
|
@ -2,7 +2,7 @@
|
||||||
Field classes
|
Field classes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from util import ValidationError, DEFAULT_ENCODING
|
from util import ValidationError, DEFAULT_ENCODING, smart_unicode
|
||||||
from widgets import TextInput, CheckboxInput, Select, SelectMultiple
|
from widgets import TextInput, CheckboxInput, Select, SelectMultiple
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
|
@ -55,10 +55,7 @@ class CharField(Field):
|
||||||
"Validates max_length and min_length. Returns a Unicode object."
|
"Validates max_length and min_length. Returns a Unicode object."
|
||||||
Field.clean(self, value)
|
Field.clean(self, value)
|
||||||
if value in EMPTY_VALUES: value = u''
|
if value in EMPTY_VALUES: value = u''
|
||||||
if not isinstance(value, basestring):
|
value = smart_unicode(value)
|
||||||
value = unicode(str(value), DEFAULT_ENCODING)
|
|
||||||
elif not isinstance(value, unicode):
|
|
||||||
value = unicode(value, DEFAULT_ENCODING)
|
|
||||||
if self.max_length is not None and len(value) > self.max_length:
|
if self.max_length is not None and len(value) > self.max_length:
|
||||||
raise ValidationError(u'Ensure this value has at most %d characters.' % self.max_length)
|
raise ValidationError(u'Ensure this value has at most %d characters.' % self.max_length)
|
||||||
if self.min_length is not None and len(value) < self.min_length:
|
if self.min_length is not None and len(value) < self.min_length:
|
||||||
|
@ -165,10 +162,7 @@ class RegexField(Field):
|
||||||
"""
|
"""
|
||||||
Field.clean(self, value)
|
Field.clean(self, value)
|
||||||
if value in EMPTY_VALUES: value = u''
|
if value in EMPTY_VALUES: value = u''
|
||||||
if not isinstance(value, basestring):
|
value = smart_unicode(value)
|
||||||
value = unicode(str(value), DEFAULT_ENCODING)
|
|
||||||
elif not isinstance(value, unicode):
|
|
||||||
value = unicode(value, DEFAULT_ENCODING)
|
|
||||||
if not self.regex.search(value):
|
if not self.regex.search(value):
|
||||||
raise ValidationError(self.error_message)
|
raise ValidationError(self.error_message)
|
||||||
return value
|
return value
|
||||||
|
@ -244,10 +238,7 @@ class ChoiceField(Field):
|
||||||
"""
|
"""
|
||||||
value = Field.clean(self, value)
|
value = Field.clean(self, value)
|
||||||
if value in EMPTY_VALUES: value = u''
|
if value in EMPTY_VALUES: value = u''
|
||||||
if not isinstance(value, basestring):
|
value = smart_unicode(value)
|
||||||
value = unicode(str(value), DEFAULT_ENCODING)
|
|
||||||
elif not isinstance(value, unicode):
|
|
||||||
value = unicode(value, DEFAULT_ENCODING)
|
|
||||||
valid_values = set([str(k) for k, v in self.choices])
|
valid_values = set([str(k) for k, v in self.choices])
|
||||||
if value not in valid_values:
|
if value not in valid_values:
|
||||||
raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % value)
|
raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % value)
|
||||||
|
@ -267,11 +258,8 @@ class MultipleChoiceField(ChoiceField):
|
||||||
raise ValidationError(u'This field is required.')
|
raise ValidationError(u'This field is required.')
|
||||||
new_value = []
|
new_value = []
|
||||||
for val in value:
|
for val in value:
|
||||||
if not isinstance(val, basestring):
|
val = smart_unicode(val)
|
||||||
value = unicode(str(val), DEFAULT_ENCODING)
|
new_value.append(val)
|
||||||
elif not isinstance(val, unicode):
|
|
||||||
value = unicode(val, DEFAULT_ENCODING)
|
|
||||||
new_value.append(value)
|
|
||||||
# Validate that each value in the value list is in self.choices.
|
# Validate that each value in the value list is in self.choices.
|
||||||
valid_values = set([k for k, v in self.choices])
|
valid_values = set([k for k, v in self.choices])
|
||||||
for val in new_value:
|
for val in new_value:
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
DEFAULT_ENCODING = 'utf-8' # TODO: First look at django.conf.settings, then fall back to this.
|
DEFAULT_ENCODING = 'utf-8' # TODO: First look at django.conf.settings, then fall back to this.
|
||||||
|
|
||||||
def smart_unicode(s):
|
def smart_unicode(s):
|
||||||
|
if not isinstance(s, basestring):
|
||||||
|
s = unicode(str(s))
|
||||||
if not isinstance(s, unicode):
|
if not isinstance(s, unicode):
|
||||||
s = unicode(s, DEFAULT_ENCODING)
|
s = unicode(s, DEFAULT_ENCODING)
|
||||||
return s
|
return s
|
||||||
|
|
|
@ -8,6 +8,7 @@ __all__ = (
|
||||||
'Select', 'SelectMultiple', 'RadioSelect',
|
'Select', 'SelectMultiple', 'RadioSelect',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from util import smart_unicode
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ except NameError:
|
||||||
|
|
||||||
# Converts a dictionary to a single string with key="value", XML-style with
|
# Converts a dictionary to a single string with key="value", XML-style with
|
||||||
# a leading space. Assumes keys do not need to be XML-escaped.
|
# a leading space. Assumes keys do not need to be XML-escaped.
|
||||||
flatatt = lambda attrs: ''.join([' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
|
flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
|
||||||
|
|
||||||
class Widget(object):
|
class Widget(object):
|
||||||
requires_data_list = False # Determines whether render()'s 'value' argument should be a list.
|
requires_data_list = False # Determines whether render()'s 'value' argument should be a list.
|
||||||
|
@ -43,7 +44,7 @@ class Input(Widget):
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
if value is None: value = ''
|
if value is None: value = ''
|
||||||
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
|
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
|
||||||
if value != '': final_attrs['value'] = value # Only add the 'value' attribute if a value is non-empty.
|
if value != '': final_attrs['value'] = smart_unicode(value) # Only add the 'value' attribute if a value is non-empty.
|
||||||
return u'<input%s />' % flatatt(final_attrs)
|
return u'<input%s />' % flatatt(final_attrs)
|
||||||
|
|
||||||
class TextInput(Input):
|
class TextInput(Input):
|
||||||
|
@ -61,6 +62,7 @@ class FileInput(Input):
|
||||||
class Textarea(Widget):
|
class Textarea(Widget):
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
if value is None: value = ''
|
if value is None: value = ''
|
||||||
|
value = smart_unicode(value)
|
||||||
final_attrs = self.build_attrs(attrs, name=name)
|
final_attrs = self.build_attrs(attrs, name=name)
|
||||||
return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))
|
return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))
|
||||||
|
|
||||||
|
@ -80,10 +82,11 @@ class Select(Widget):
|
||||||
if value is None: value = ''
|
if value is None: value = ''
|
||||||
final_attrs = self.build_attrs(attrs, name=name)
|
final_attrs = self.build_attrs(attrs, name=name)
|
||||||
output = [u'<select%s>' % flatatt(final_attrs)]
|
output = [u'<select%s>' % flatatt(final_attrs)]
|
||||||
str_value = str(value) # Normalize to string.
|
str_value = smart_unicode(value) # Normalize to string.
|
||||||
for option_value, option_label in chain(self.choices, choices):
|
for option_value, option_label in chain(self.choices, choices):
|
||||||
selected_html = (str(option_value) == str_value) and ' selected="selected"' or ''
|
option_value = smart_unicode(option_value)
|
||||||
output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(option_label)))
|
selected_html = (option_value == str_value) and u' selected="selected"' or ''
|
||||||
|
output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(smart_unicode(option_label))))
|
||||||
output.append(u'</select>')
|
output.append(u'</select>')
|
||||||
return u'\n'.join(output)
|
return u'\n'.join(output)
|
||||||
|
|
||||||
|
@ -98,10 +101,11 @@ class SelectMultiple(Widget):
|
||||||
if value is None: value = []
|
if value is None: value = []
|
||||||
final_attrs = self.build_attrs(attrs, name=name)
|
final_attrs = self.build_attrs(attrs, name=name)
|
||||||
output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)]
|
output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)]
|
||||||
str_values = set([str(v) for v in value]) # Normalize to strings.
|
str_values = set([smart_unicode(v) for v in value]) # Normalize to strings.
|
||||||
for option_value, option_label in chain(self.choices, choices):
|
for option_value, option_label in chain(self.choices, choices):
|
||||||
selected_html = (str(option_value) in str_values) and ' selected="selected"' or ''
|
option_value = smart_unicode(option_value)
|
||||||
output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(option_label)))
|
selected_html = (option_value in str_values) and ' selected="selected"' or ''
|
||||||
|
output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(smart_unicode(option_label))))
|
||||||
output.append(u'</select>')
|
output.append(u'</select>')
|
||||||
return u'\n'.join(output)
|
return u'\n'.join(output)
|
||||||
|
|
||||||
|
@ -116,7 +120,7 @@ class RadioInput(object):
|
||||||
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
||||||
|
|
||||||
def is_checked(self):
|
def is_checked(self):
|
||||||
return self.value == str(self.choice_value)
|
return self.value == smart_unicode(self.choice_value)
|
||||||
|
|
||||||
def tag(self):
|
def tag(self):
|
||||||
final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
|
final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
|
||||||
|
@ -142,7 +146,7 @@ class RadioSelect(Select):
|
||||||
def render(self, name, value, attrs=None, choices=()):
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
"Returns a RadioFieldRenderer instance rather than a Unicode string."
|
"Returns a RadioFieldRenderer instance rather than a Unicode string."
|
||||||
if value is None: value = ''
|
if value is None: value = ''
|
||||||
str_value = str(value) # Normalize to string.
|
str_value = smart_unicode(value) # Normalize to string.
|
||||||
return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices)))
|
return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices)))
|
||||||
|
|
||||||
class CheckboxSelectMultiple(Widget):
|
class CheckboxSelectMultiple(Widget):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""
|
# -*- coding: utf-8 -*-
|
||||||
|
r"""
|
||||||
>>> from django.newforms import *
|
>>> from django.newforms import *
|
||||||
>>> import datetime
|
>>> import datetime
|
||||||
>>> import re
|
>>> import re
|
||||||
|
@ -17,6 +18,11 @@ u'<input type="text" name="email" value="some "quoted" & ampersand
|
||||||
>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
|
>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
|
||||||
u'<input type="text" name="email" value="test@example.com" class="fun" />'
|
u'<input type="text" name="email" value="test@example.com" class="fun" />'
|
||||||
|
|
||||||
|
# Note that doctest in Python 2.4 (and maybe 2.5?) doesn't support non-ascii
|
||||||
|
# characters in output, so we're displaying the repr() here.
|
||||||
|
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
|
||||||
|
u'<input type="text" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" class="fun" />'
|
||||||
|
|
||||||
You can also pass 'attrs' to the constructor:
|
You can also pass 'attrs' to the constructor:
|
||||||
>>> w = TextInput(attrs={'class': 'fun'})
|
>>> w = TextInput(attrs={'class': 'fun'})
|
||||||
>>> w.render('email', '')
|
>>> w.render('email', '')
|
||||||
|
@ -55,6 +61,9 @@ u'<input type="password" class="fun" value="foo@example.com" name="email" />'
|
||||||
>>> w.render('email', '', attrs={'class': 'special'})
|
>>> w.render('email', '', attrs={'class': 'special'})
|
||||||
u'<input type="password" class="special" name="email" />'
|
u'<input type="password" class="special" name="email" />'
|
||||||
|
|
||||||
|
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
|
||||||
|
u'<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
|
||||||
|
|
||||||
# HiddenInput Widget ############################################################
|
# HiddenInput Widget ############################################################
|
||||||
|
|
||||||
>>> w = HiddenInput()
|
>>> w = HiddenInput()
|
||||||
|
@ -81,6 +90,14 @@ u'<input type="hidden" class="fun" value="foo@example.com" name="email" />'
|
||||||
>>> w.render('email', '', attrs={'class': 'special'})
|
>>> w.render('email', '', attrs={'class': 'special'})
|
||||||
u'<input type="hidden" class="special" name="email" />'
|
u'<input type="hidden" class="special" name="email" />'
|
||||||
|
|
||||||
|
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
|
||||||
|
u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
|
||||||
|
|
||||||
|
'attrs' passed to render() get precedence over those passed to the constructor:
|
||||||
|
>>> w = HiddenInput(attrs={'class': 'pretty'})
|
||||||
|
>>> w.render('email', '', attrs={'class': 'special'})
|
||||||
|
u'<input type="hidden" class="special" name="email" />'
|
||||||
|
|
||||||
# FileInput Widget ############################################################
|
# FileInput Widget ############################################################
|
||||||
|
|
||||||
>>> w = FileInput()
|
>>> w = FileInput()
|
||||||
|
@ -102,10 +119,8 @@ u'<input type="file" class="fun" name="email" />'
|
||||||
>>> w.render('email', 'foo@example.com')
|
>>> w.render('email', 'foo@example.com')
|
||||||
u'<input type="file" class="fun" value="foo@example.com" name="email" />'
|
u'<input type="file" class="fun" value="foo@example.com" name="email" />'
|
||||||
|
|
||||||
'attrs' passed to render() get precedence over those passed to the constructor:
|
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
|
||||||
>>> w = HiddenInput(attrs={'class': 'pretty'})
|
u'<input type="file" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
|
||||||
>>> w.render('email', '', attrs={'class': 'special'})
|
|
||||||
u'<input type="hidden" class="special" name="email" />'
|
|
||||||
|
|
||||||
# Textarea Widget #############################################################
|
# Textarea Widget #############################################################
|
||||||
|
|
||||||
|
@ -133,6 +148,9 @@ u'<textarea class="pretty" name="msg">example</textarea>'
|
||||||
>>> w.render('msg', '', attrs={'class': 'special'})
|
>>> w.render('msg', '', attrs={'class': 'special'})
|
||||||
u'<textarea class="special" name="msg"></textarea>'
|
u'<textarea class="special" name="msg"></textarea>'
|
||||||
|
|
||||||
|
>>> w.render('msg', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
|
||||||
|
u'<textarea class="fun" name="msg">\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</textarea>'
|
||||||
|
|
||||||
# CheckboxInput Widget ########################################################
|
# CheckboxInput Widget ########################################################
|
||||||
|
|
||||||
>>> w = CheckboxInput()
|
>>> w = CheckboxInput()
|
||||||
|
@ -236,6 +254,9 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
|
||||||
<option value="5">5</option>
|
<option value="5">5</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
>>> w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
|
||||||
|
u'<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
|
||||||
|
|
||||||
# SelectMultiple Widget #######################################################
|
# SelectMultiple Widget #######################################################
|
||||||
|
|
||||||
>>> w = SelectMultiple()
|
>>> w = SelectMultiple()
|
||||||
|
@ -340,6 +361,9 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
|
||||||
<option value="5">5</option>
|
<option value="5">5</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
>>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
|
||||||
|
u'<select multiple="multiple" name="nums">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
|
||||||
|
|
||||||
# RadioSelect Widget ##########################################################
|
# RadioSelect Widget ##########################################################
|
||||||
|
|
||||||
>>> w = RadioSelect()
|
>>> w = RadioSelect()
|
||||||
|
|
Loading…
Reference in New Issue