Loading django/forms/fields.py +6 −16 Original line number Diff line number Diff line Loading @@ -184,17 +184,13 @@ class Field(object): # For purposes of seeing whether something has changed, None is # the same as an empty string, if the data or inital value we get # is None, replace it w/ ''. if data is None: data_value = '' else: data_value = data if initial is None: initial_value = '' else: initial_value = initial if force_text(initial_value) != force_text(data_value): initial_value = initial if initial is not None else '' try: data = self.to_python(data) except ValidationError: return True return False data_value = data if data is not None else '' return initial_value != data_value def __deepcopy__(self, memo): result = copy.copy(self) Loading Loading @@ -392,12 +388,6 @@ class BaseTemporalField(Field): def strptime(self, value, format): raise NotImplementedError('Subclasses must define this method.') def _has_changed(self, initial, data): try: data = self.to_python(data) except ValidationError: return True return self.to_python(initial) != data class DateField(BaseTemporalField): widget = DateInput Loading django/forms/forms.py +2 −2 Original line number Diff line number Diff line Loading @@ -345,8 +345,8 @@ class BaseForm(object): else: initial_prefixed_name = self.add_initial_prefix(name) hidden_widget = field.hidden_widget() initial_value = hidden_widget.value_from_datadict( self.data, self.files, initial_prefixed_name) initial_value = field.to_python(hidden_widget.value_from_datadict( self.data, self.files, initial_prefixed_name)) if hasattr(field.widget, '_has_changed'): warnings.warn("The _has_changed method on widgets is deprecated," " define it at field level instead.", Loading django/forms/models.py +16 −0 Original line number Diff line number Diff line Loading @@ -1012,6 +1012,11 @@ class ModelChoiceField(ChoiceField): def validate(self, value): return Field.validate(self, value) def _has_changed(self, initial, data): initial_value = initial if initial is not None else '' data_value = data if data is not None else '' return force_text(self.prepare_value(initial_value)) != force_text(data_value) class ModelMultipleChoiceField(ModelChoiceField): """A MultipleChoiceField whose choices are a model QuerySet.""" widget = SelectMultiple Loading Loading @@ -1059,3 +1064,14 @@ class ModelMultipleChoiceField(ModelChoiceField): not hasattr(value, '_meta')): return [super(ModelMultipleChoiceField, self).prepare_value(v) for v in value] return super(ModelMultipleChoiceField, self).prepare_value(value) def _has_changed(self, initial, data): if initial is None: initial = [] if data is None: data = [] if len(initial) != len(data): return True initial_set = set([force_text(value) for value in initial]) data_set = set([force_text(value) for value in data]) return data_set != initial_set tests/forms_tests/tests/fields.py +27 −9 Original line number Diff line number Diff line Loading @@ -35,7 +35,9 @@ from decimal import Decimal from django.core.files.uploadedfile import SimpleUploadedFile from django.forms import * from django.test import SimpleTestCase from django.utils import formats from django.utils import six from django.utils import translation from django.utils._os import upath Loading Loading @@ -256,6 +258,17 @@ class FieldsTests(SimpleTestCase): f = FloatField(localize=True) self.assertWidgetRendersTo(f, '<input id="id_f" name="f" type="text" />') def test_floatfield_changed(self): f = FloatField() n = 4.35 self.assertFalse(f._has_changed(n, '4.3500')) with translation.override('fr'): with self.settings(USE_L10N=True): f = FloatField(localize=True) localized_n = formats.localize_input(n) # -> '4,35' in French self.assertFalse(f._has_changed(n, localized_n)) # DecimalField ################################################################ def test_decimalfield_1(self): Loading Loading @@ -346,6 +359,18 @@ class FieldsTests(SimpleTestCase): f = DecimalField(localize=True) self.assertWidgetRendersTo(f, '<input id="id_f" name="f" type="text" />') def test_decimalfield_changed(self): f = DecimalField(max_digits=2, decimal_places=2) d = Decimal("0.1") self.assertFalse(f._has_changed(d, '0.10')) self.assertTrue(f._has_changed(d, '0.101')) with translation.override('fr'): with self.settings(USE_L10N=True): f = DecimalField(max_digits=2, decimal_places=2, localize=True) localized_d = formats.localize_input(d) # -> '0,1' in French self.assertFalse(f._has_changed(d, localized_d)) # DateField ################################################################### def test_datefield_1(self): Loading Loading @@ -404,7 +429,6 @@ class FieldsTests(SimpleTestCase): f = DateField(input_formats=[format]) d = datetime.date(2007, 9, 17) self.assertFalse(f._has_changed(d, '17/09/2007')) self.assertFalse(f._has_changed(d.strftime(format), '17/09/2007')) def test_datefield_strptime(self): """Test that field.strptime doesn't raise an UnicodeEncodeError (#16123)""" Loading Loading @@ -445,14 +469,10 @@ class FieldsTests(SimpleTestCase): def test_timefield_changed(self): t1 = datetime.time(12, 51, 34, 482548) t2 = datetime.time(12, 51) format = '%H:%M' f = TimeField(input_formats=[format]) f = TimeField(input_formats=['%H:%M', '%H:%M %p']) self.assertTrue(f._has_changed(t1, '12:51')) self.assertFalse(f._has_changed(t2, '12:51')) format = '%I:%M %p' f = TimeField(input_formats=[format]) self.assertFalse(f._has_changed(t2.strftime(format), '12:51 PM')) self.assertFalse(f._has_changed(t2, '12:51 PM')) # DateTimeField ############################################################### Loading Loading @@ -518,8 +538,6 @@ class FieldsTests(SimpleTestCase): f = DateTimeField(input_formats=[format]) d = datetime.datetime(2006, 9, 17, 14, 30, 0) self.assertFalse(f._has_changed(d, '2006 09 17 2:30 PM')) # Initial value may be a string from a hidden input self.assertFalse(f._has_changed(d.strftime(format), '2006 09 17 2:30 PM')) # RegexField ################################################################## Loading tests/i18n/tests.py +2 −1 Original line number Diff line number Diff line Loading @@ -15,7 +15,7 @@ from django.test.utils import override_settings from django.utils import translation from django.utils.formats import (get_format, date_format, time_format, localize, localize_input, iter_format_modules, get_format_modules, number_format, sanitize_separators) number_format, reset_format_cache, sanitize_separators) from django.utils.importlib import import_module from django.utils.numberformat import format as nformat from django.utils._os import upath Loading Loading @@ -463,6 +463,7 @@ class FormattingTests(TestCase): fr_formats.THOUSAND_SEPARATOR = '' fr_formats.FIRST_DAY_OF_WEEK = 0 reset_format_cache() with translation.override('fr'): with self.settings(USE_THOUSAND_SEPARATOR=True, THOUSAND_SEPARATOR='!'): self.assertEqual('', get_format('THOUSAND_SEPARATOR')) Loading Loading
django/forms/fields.py +6 −16 Original line number Diff line number Diff line Loading @@ -184,17 +184,13 @@ class Field(object): # For purposes of seeing whether something has changed, None is # the same as an empty string, if the data or inital value we get # is None, replace it w/ ''. if data is None: data_value = '' else: data_value = data if initial is None: initial_value = '' else: initial_value = initial if force_text(initial_value) != force_text(data_value): initial_value = initial if initial is not None else '' try: data = self.to_python(data) except ValidationError: return True return False data_value = data if data is not None else '' return initial_value != data_value def __deepcopy__(self, memo): result = copy.copy(self) Loading Loading @@ -392,12 +388,6 @@ class BaseTemporalField(Field): def strptime(self, value, format): raise NotImplementedError('Subclasses must define this method.') def _has_changed(self, initial, data): try: data = self.to_python(data) except ValidationError: return True return self.to_python(initial) != data class DateField(BaseTemporalField): widget = DateInput Loading
django/forms/forms.py +2 −2 Original line number Diff line number Diff line Loading @@ -345,8 +345,8 @@ class BaseForm(object): else: initial_prefixed_name = self.add_initial_prefix(name) hidden_widget = field.hidden_widget() initial_value = hidden_widget.value_from_datadict( self.data, self.files, initial_prefixed_name) initial_value = field.to_python(hidden_widget.value_from_datadict( self.data, self.files, initial_prefixed_name)) if hasattr(field.widget, '_has_changed'): warnings.warn("The _has_changed method on widgets is deprecated," " define it at field level instead.", Loading
django/forms/models.py +16 −0 Original line number Diff line number Diff line Loading @@ -1012,6 +1012,11 @@ class ModelChoiceField(ChoiceField): def validate(self, value): return Field.validate(self, value) def _has_changed(self, initial, data): initial_value = initial if initial is not None else '' data_value = data if data is not None else '' return force_text(self.prepare_value(initial_value)) != force_text(data_value) class ModelMultipleChoiceField(ModelChoiceField): """A MultipleChoiceField whose choices are a model QuerySet.""" widget = SelectMultiple Loading Loading @@ -1059,3 +1064,14 @@ class ModelMultipleChoiceField(ModelChoiceField): not hasattr(value, '_meta')): return [super(ModelMultipleChoiceField, self).prepare_value(v) for v in value] return super(ModelMultipleChoiceField, self).prepare_value(value) def _has_changed(self, initial, data): if initial is None: initial = [] if data is None: data = [] if len(initial) != len(data): return True initial_set = set([force_text(value) for value in initial]) data_set = set([force_text(value) for value in data]) return data_set != initial_set
tests/forms_tests/tests/fields.py +27 −9 Original line number Diff line number Diff line Loading @@ -35,7 +35,9 @@ from decimal import Decimal from django.core.files.uploadedfile import SimpleUploadedFile from django.forms import * from django.test import SimpleTestCase from django.utils import formats from django.utils import six from django.utils import translation from django.utils._os import upath Loading Loading @@ -256,6 +258,17 @@ class FieldsTests(SimpleTestCase): f = FloatField(localize=True) self.assertWidgetRendersTo(f, '<input id="id_f" name="f" type="text" />') def test_floatfield_changed(self): f = FloatField() n = 4.35 self.assertFalse(f._has_changed(n, '4.3500')) with translation.override('fr'): with self.settings(USE_L10N=True): f = FloatField(localize=True) localized_n = formats.localize_input(n) # -> '4,35' in French self.assertFalse(f._has_changed(n, localized_n)) # DecimalField ################################################################ def test_decimalfield_1(self): Loading Loading @@ -346,6 +359,18 @@ class FieldsTests(SimpleTestCase): f = DecimalField(localize=True) self.assertWidgetRendersTo(f, '<input id="id_f" name="f" type="text" />') def test_decimalfield_changed(self): f = DecimalField(max_digits=2, decimal_places=2) d = Decimal("0.1") self.assertFalse(f._has_changed(d, '0.10')) self.assertTrue(f._has_changed(d, '0.101')) with translation.override('fr'): with self.settings(USE_L10N=True): f = DecimalField(max_digits=2, decimal_places=2, localize=True) localized_d = formats.localize_input(d) # -> '0,1' in French self.assertFalse(f._has_changed(d, localized_d)) # DateField ################################################################### def test_datefield_1(self): Loading Loading @@ -404,7 +429,6 @@ class FieldsTests(SimpleTestCase): f = DateField(input_formats=[format]) d = datetime.date(2007, 9, 17) self.assertFalse(f._has_changed(d, '17/09/2007')) self.assertFalse(f._has_changed(d.strftime(format), '17/09/2007')) def test_datefield_strptime(self): """Test that field.strptime doesn't raise an UnicodeEncodeError (#16123)""" Loading Loading @@ -445,14 +469,10 @@ class FieldsTests(SimpleTestCase): def test_timefield_changed(self): t1 = datetime.time(12, 51, 34, 482548) t2 = datetime.time(12, 51) format = '%H:%M' f = TimeField(input_formats=[format]) f = TimeField(input_formats=['%H:%M', '%H:%M %p']) self.assertTrue(f._has_changed(t1, '12:51')) self.assertFalse(f._has_changed(t2, '12:51')) format = '%I:%M %p' f = TimeField(input_formats=[format]) self.assertFalse(f._has_changed(t2.strftime(format), '12:51 PM')) self.assertFalse(f._has_changed(t2, '12:51 PM')) # DateTimeField ############################################################### Loading Loading @@ -518,8 +538,6 @@ class FieldsTests(SimpleTestCase): f = DateTimeField(input_formats=[format]) d = datetime.datetime(2006, 9, 17, 14, 30, 0) self.assertFalse(f._has_changed(d, '2006 09 17 2:30 PM')) # Initial value may be a string from a hidden input self.assertFalse(f._has_changed(d.strftime(format), '2006 09 17 2:30 PM')) # RegexField ################################################################## Loading
tests/i18n/tests.py +2 −1 Original line number Diff line number Diff line Loading @@ -15,7 +15,7 @@ from django.test.utils import override_settings from django.utils import translation from django.utils.formats import (get_format, date_format, time_format, localize, localize_input, iter_format_modules, get_format_modules, number_format, sanitize_separators) number_format, reset_format_cache, sanitize_separators) from django.utils.importlib import import_module from django.utils.numberformat import format as nformat from django.utils._os import upath Loading Loading @@ -463,6 +463,7 @@ class FormattingTests(TestCase): fr_formats.THOUSAND_SEPARATOR = '' fr_formats.FIRST_DAY_OF_WEEK = 0 reset_format_cache() with translation.override('fr'): with self.settings(USE_THOUSAND_SEPARATOR=True, THOUSAND_SEPARATOR='!'): self.assertEqual('', get_format('THOUSAND_SEPARATOR')) Loading