Fixed #23162 -- Renamed forms.Field._has_changed() to has_changed().
This commit is contained in:
parent
99561eef26
commit
deed00c0d8
|
@ -64,7 +64,7 @@ class ReadOnlyPasswordHashField(forms.Field):
|
|||
# render an input field.
|
||||
return initial
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
def has_changed(self, initial, data):
|
||||
return False
|
||||
|
||||
|
||||
|
|
|
@ -533,4 +533,4 @@ class ReadOnlyPasswordHashTest(TestCase):
|
|||
|
||||
def test_readonly_field_has_changed(self):
|
||||
field = ReadOnlyPasswordHashField()
|
||||
self.assertFalse(field._has_changed('aaa', 'bbb'))
|
||||
self.assertFalse(field.has_changed('aaa', 'bbb'))
|
||||
|
|
|
@ -81,7 +81,7 @@ class GeometryField(forms.Field):
|
|||
|
||||
return geom
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
def has_changed(self, initial, data):
|
||||
""" Compare geographic value of data with its initial value. """
|
||||
|
||||
try:
|
||||
|
|
|
@ -44,7 +44,7 @@ class GeoAdminTest(TestCase):
|
|||
"""
|
||||
geoadmin = admin.site._registry[City]
|
||||
form = geoadmin.get_changelist_form(None)()
|
||||
has_changed = form.fields['point']._has_changed
|
||||
has_changed = form.fields['point'].has_changed
|
||||
|
||||
initial = Point(13.4197458572965953, 52.5194108501149799, srid=4326)
|
||||
data_same = "SRID=3857;POINT(1493879.2754093995 6894592.019687599)"
|
||||
|
|
|
@ -25,7 +25,7 @@ from django.forms.widgets import (
|
|||
from django.utils import formats
|
||||
from django.utils.encoding import smart_text, force_str, force_text
|
||||
from django.utils.ipv6 import clean_ipv6_address
|
||||
from django.utils.deprecation import RemovedInDjango19Warning, RemovedInDjango20Warning
|
||||
from django.utils.deprecation import RemovedInDjango19Warning, RemovedInDjango20Warning, RenameMethodsBase
|
||||
from django.utils import six
|
||||
from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit
|
||||
from django.utils.translation import ugettext_lazy as _, ungettext_lazy
|
||||
|
@ -45,7 +45,13 @@ __all__ = (
|
|||
)
|
||||
|
||||
|
||||
class Field(object):
|
||||
class RenameFieldMethods(RenameMethodsBase):
|
||||
renamed_methods = (
|
||||
('_has_changed', 'has_changed', RemovedInDjango20Warning),
|
||||
)
|
||||
|
||||
|
||||
class Field(six.with_metaclass(RenameFieldMethods, object)):
|
||||
widget = TextInput # Default widget to use when rendering this type of Field.
|
||||
hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
|
||||
default_validators = [] # Default set of validators
|
||||
|
@ -185,7 +191,7 @@ class Field(object):
|
|||
return self.limit_choices_to()
|
||||
return self.limit_choices_to
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
def has_changed(self, initial, data):
|
||||
"""
|
||||
Return True if data differs from initial.
|
||||
"""
|
||||
|
@ -629,7 +635,7 @@ class FileField(Field):
|
|||
return initial
|
||||
return data
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
def has_changed(self, initial, data):
|
||||
if data is None:
|
||||
return False
|
||||
return True
|
||||
|
@ -744,7 +750,7 @@ class BooleanField(Field):
|
|||
if not value and self.required:
|
||||
raise ValidationError(self.error_messages['required'], code='required')
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
def has_changed(self, initial, data):
|
||||
# Sometimes data or initial could be None or '' which should be the
|
||||
# same thing as False.
|
||||
if initial == 'False':
|
||||
|
@ -779,7 +785,7 @@ class NullBooleanField(BooleanField):
|
|||
def validate(self, value):
|
||||
pass
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
def has_changed(self, initial, data):
|
||||
# None (unknown) and False (No) are not the same
|
||||
if initial is not None:
|
||||
initial = bool(initial)
|
||||
|
@ -906,7 +912,7 @@ class MultipleChoiceField(ChoiceField):
|
|||
params={'value': val},
|
||||
)
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
def has_changed(self, initial, data):
|
||||
if initial is None:
|
||||
initial = []
|
||||
if data is None:
|
||||
|
@ -1084,14 +1090,14 @@ class MultiValueField(Field):
|
|||
"""
|
||||
raise NotImplementedError('Subclasses must implement this method.')
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
def has_changed(self, initial, data):
|
||||
if initial is None:
|
||||
initial = ['' for x in range(0, len(data))]
|
||||
else:
|
||||
if not isinstance(initial, list):
|
||||
initial = self.widget.decompress(initial)
|
||||
for field, initial, data in zip(self.fields, initial, data):
|
||||
if field._has_changed(field.to_python(initial), data):
|
||||
if field.has_changed(field.to_python(initial), data):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
|
@ -443,7 +443,7 @@ class BaseForm(object):
|
|||
# Always assume data has changed if validation fails.
|
||||
self._changed_data.append(name)
|
||||
continue
|
||||
if field._has_changed(initial_value, data_value):
|
||||
if field.has_changed(initial_value, data_value):
|
||||
self._changed_data.append(name)
|
||||
return self._changed_data
|
||||
|
||||
|
|
|
@ -1058,7 +1058,7 @@ class InlineForeignKeyField(Field):
|
|||
raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')
|
||||
return self.parent_instance
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
def has_changed(self, initial, data):
|
||||
return False
|
||||
|
||||
|
||||
|
@ -1186,7 +1186,7 @@ class ModelChoiceField(ChoiceField):
|
|||
def validate(self, value):
|
||||
return Field.validate(self, value)
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
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)
|
||||
|
@ -1254,7 +1254,7 @@ class ModelMultipleChoiceField(ModelChoiceField):
|
|||
return [super(ModelMultipleChoiceField, self).prepare_value(v) for v in value]
|
||||
return super(ModelMultipleChoiceField, self).prepare_value(value)
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
def has_changed(self, initial, data):
|
||||
if initial is None:
|
||||
initial = []
|
||||
if data is None:
|
||||
|
|
|
@ -46,6 +46,9 @@ about each item can often be found in the release notes of two versions prior.
|
|||
|
||||
* Support for string ``view`` arguments to ``url()`` will be removed.
|
||||
|
||||
* The backward compatible shim to rename ``django.forms.Form._has_changed()``
|
||||
to ``has_changed()`` will be removed.
|
||||
|
||||
.. _deprecation-removed-in-1.9:
|
||||
|
||||
1.9
|
||||
|
|
|
@ -279,7 +279,9 @@ so that the comparison can be done:
|
|||
>>> f.has_changed()
|
||||
|
||||
``has_changed()`` will be ``True`` if the data from ``request.POST`` differs
|
||||
from what was provided in :attr:`~Form.initial` or ``False`` otherwise.
|
||||
from what was provided in :attr:`~Form.initial` or ``False`` otherwise. The
|
||||
result is computed by calling :meth:`Field.has_changed` for each field in the
|
||||
form.
|
||||
|
||||
Accessing the fields from the form
|
||||
----------------------------------
|
||||
|
|
|
@ -299,6 +299,24 @@ as the rendered output.
|
|||
See the :ref:`format localization <format-localization>` documentation for
|
||||
more information.
|
||||
|
||||
|
||||
Checking if the field data has changed
|
||||
--------------------------------------
|
||||
|
||||
``has_changed()``
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. method:: Field.has_changed()
|
||||
|
||||
.. versionchanged:: 1.8
|
||||
|
||||
This method was renamed from ``_has_changed()``.
|
||||
|
||||
The ``has_changed()`` method is used to determine if the field value has changed
|
||||
from the initial value. Returns ``True`` or ``False``.
|
||||
|
||||
See the :class:`Form.has_changed()` documentation for more information.
|
||||
|
||||
.. _built-in-fields:
|
||||
|
||||
Built-in ``Field`` classes
|
||||
|
|
|
@ -676,3 +676,9 @@ An older (pre-1.0), more restrictive and verbose input format for the
|
|||
Using the new syntax, this becomes::
|
||||
|
||||
``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``
|
||||
|
||||
``django.forms.Field._has_changed()``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Rename this method to :meth:`~django.forms.Field.has_changed` by removing the
|
||||
leading underscore. The old name will still work until Django 2.0.
|
||||
|
|
|
@ -443,19 +443,19 @@ class FormsExtraTestCase(TestCase, AssertFormErrorsMixin):
|
|||
self.assertFormErrors(['This field is required.'], f.clean, ['some text', ['JP']])
|
||||
|
||||
# test with no initial data
|
||||
self.assertTrue(f._has_changed(None, ['some text', ['J', 'P'], ['2007-04-25', '6:24:00']]))
|
||||
self.assertTrue(f.has_changed(None, ['some text', ['J', 'P'], ['2007-04-25', '6:24:00']]))
|
||||
|
||||
# test when the data is the same as initial
|
||||
self.assertFalse(f._has_changed('some text,JP,2007-04-25 06:24:00',
|
||||
self.assertFalse(f.has_changed('some text,JP,2007-04-25 06:24:00',
|
||||
['some text', ['J', 'P'], ['2007-04-25', '6:24:00']]))
|
||||
|
||||
# test when the first widget's data has changed
|
||||
self.assertTrue(f._has_changed('some text,JP,2007-04-25 06:24:00',
|
||||
self.assertTrue(f.has_changed('some text,JP,2007-04-25 06:24:00',
|
||||
['other text', ['J', 'P'], ['2007-04-25', '6:24:00']]))
|
||||
|
||||
# test when the last widget's data has changed. this ensures that it is not
|
||||
# short circuiting while testing the widgets.
|
||||
self.assertTrue(f._has_changed('some text,JP,2007-04-25 06:24:00',
|
||||
self.assertTrue(f.has_changed('some text,JP,2007-04-25 06:24:00',
|
||||
['some text', ['J', 'P'], ['2009-04-25', '11:44:00']]))
|
||||
|
||||
class ComplexFieldForm(Form):
|
||||
|
@ -803,7 +803,7 @@ class FormsExtraL10NTestCase(TestCase):
|
|||
|
||||
def test_l10n_date_changed(self):
|
||||
"""
|
||||
Ensure that DateField._has_changed() with SelectDateWidget works
|
||||
Ensure that DateField.has_changed() with SelectDateWidget works
|
||||
correctly with a localized date format.
|
||||
Refs #17165.
|
||||
"""
|
||||
|
|
|
@ -315,12 +315,12 @@ class FieldsTests(SimpleTestCase):
|
|||
def test_floatfield_changed(self):
|
||||
f = FloatField()
|
||||
n = 4.35
|
||||
self.assertFalse(f._has_changed(n, '4.3500'))
|
||||
self.assertFalse(f.has_changed(n, '4.3500'))
|
||||
|
||||
with translation.override('fr'), 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))
|
||||
self.assertFalse(f.has_changed(n, localized_n))
|
||||
|
||||
# DecimalField ################################################################
|
||||
|
||||
|
@ -428,13 +428,13 @@ class FieldsTests(SimpleTestCase):
|
|||
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'))
|
||||
self.assertFalse(f.has_changed(d, '0.10'))
|
||||
self.assertTrue(f.has_changed(d, '0.101'))
|
||||
|
||||
with translation.override('fr'), 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))
|
||||
self.assertFalse(f.has_changed(d, localized_d))
|
||||
|
||||
# DateField ###################################################################
|
||||
|
||||
|
@ -493,7 +493,11 @@ class FieldsTests(SimpleTestCase):
|
|||
format = '%d/%m/%Y'
|
||||
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, '17/09/2007'))
|
||||
# Test for deprecated behavior _has_changed
|
||||
with warnings.catch_warnings(record=True):
|
||||
warnings.simplefilter("always")
|
||||
self.assertFalse(f._has_changed(d, '17/09/2007'))
|
||||
|
||||
def test_datefield_strptime(self):
|
||||
"""Test that field.strptime doesn't raise an UnicodeEncodeError (#16123)"""
|
||||
|
@ -535,9 +539,9 @@ class FieldsTests(SimpleTestCase):
|
|||
t1 = datetime.time(12, 51, 34, 482548)
|
||||
t2 = datetime.time(12, 51)
|
||||
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'))
|
||||
self.assertFalse(f._has_changed(t2, '12:51 PM'))
|
||||
self.assertTrue(f.has_changed(t1, '12:51'))
|
||||
self.assertFalse(f.has_changed(t2, '12:51'))
|
||||
self.assertFalse(f.has_changed(t2, '12:51 PM'))
|
||||
|
||||
# DateTimeField ###############################################################
|
||||
|
||||
|
@ -602,7 +606,7 @@ class FieldsTests(SimpleTestCase):
|
|||
format = '%Y %m %d %I:%M %p'
|
||||
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'))
|
||||
self.assertFalse(f.has_changed(d, '2006 09 17 2:30 PM'))
|
||||
|
||||
# RegexField ##################################################################
|
||||
|
||||
|
@ -731,7 +735,7 @@ class FieldsTests(SimpleTestCase):
|
|||
|
||||
def test_filefield_changed(self):
|
||||
'''
|
||||
Test for the behavior of _has_changed for FileField. The value of data will
|
||||
Test for the behavior of has_changed for FileField. The value of data will
|
||||
more than likely come from request.FILES. The value of initial data will
|
||||
likely be a filename stored in the database. Since its value is of no use to
|
||||
a FileField it is ignored.
|
||||
|
@ -739,17 +743,17 @@ class FieldsTests(SimpleTestCase):
|
|||
f = FileField()
|
||||
|
||||
# No file was uploaded and no initial data.
|
||||
self.assertFalse(f._has_changed('', None))
|
||||
self.assertFalse(f.has_changed('', None))
|
||||
|
||||
# A file was uploaded and no initial data.
|
||||
self.assertTrue(f._has_changed('', {'filename': 'resume.txt', 'content': 'My resume'}))
|
||||
self.assertTrue(f.has_changed('', {'filename': 'resume.txt', 'content': 'My resume'}))
|
||||
|
||||
# A file was not uploaded, but there is initial data
|
||||
self.assertFalse(f._has_changed('resume.txt', None))
|
||||
self.assertFalse(f.has_changed('resume.txt', None))
|
||||
|
||||
# A file was uploaded and there is initial data (file identity is not dealt
|
||||
# with here)
|
||||
self.assertTrue(f._has_changed('resume.txt', {'filename': 'resume.txt', 'content': 'My resume'}))
|
||||
self.assertTrue(f.has_changed('resume.txt', {'filename': 'resume.txt', 'content': 'My resume'}))
|
||||
|
||||
# ImageField ##################################################################
|
||||
|
||||
|
@ -913,15 +917,15 @@ class FieldsTests(SimpleTestCase):
|
|||
|
||||
def test_booleanfield_changed(self):
|
||||
f = BooleanField()
|
||||
self.assertFalse(f._has_changed(None, None))
|
||||
self.assertFalse(f._has_changed(None, ''))
|
||||
self.assertFalse(f._has_changed('', None))
|
||||
self.assertFalse(f._has_changed('', ''))
|
||||
self.assertTrue(f._has_changed(False, 'on'))
|
||||
self.assertFalse(f._has_changed(True, 'on'))
|
||||
self.assertTrue(f._has_changed(True, ''))
|
||||
self.assertFalse(f.has_changed(None, None))
|
||||
self.assertFalse(f.has_changed(None, ''))
|
||||
self.assertFalse(f.has_changed('', None))
|
||||
self.assertFalse(f.has_changed('', ''))
|
||||
self.assertTrue(f.has_changed(False, 'on'))
|
||||
self.assertFalse(f.has_changed(True, 'on'))
|
||||
self.assertTrue(f.has_changed(True, ''))
|
||||
# Initial value may have mutated to a string due to show_hidden_initial (#19537)
|
||||
self.assertTrue(f._has_changed('False', 'on'))
|
||||
self.assertTrue(f.has_changed('False', 'on'))
|
||||
|
||||
# ChoiceField #################################################################
|
||||
|
||||
|
@ -996,8 +1000,8 @@ class FieldsTests(SimpleTestCase):
|
|||
def test_typedchoicefield_has_changed(self):
|
||||
# has_changed should not trigger required validation
|
||||
f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=True)
|
||||
self.assertFalse(f._has_changed(None, ''))
|
||||
self.assertFalse(f._has_changed(1, '1'))
|
||||
self.assertFalse(f.has_changed(None, ''))
|
||||
self.assertFalse(f.has_changed(1, '1'))
|
||||
|
||||
def test_typedchoicefield_special_coerce(self):
|
||||
"""
|
||||
|
@ -1065,13 +1069,13 @@ class FieldsTests(SimpleTestCase):
|
|||
|
||||
def test_nullbooleanfield_changed(self):
|
||||
f = NullBooleanField()
|
||||
self.assertTrue(f._has_changed(False, None))
|
||||
self.assertTrue(f._has_changed(None, False))
|
||||
self.assertFalse(f._has_changed(None, None))
|
||||
self.assertFalse(f._has_changed(False, False))
|
||||
self.assertTrue(f._has_changed(True, False))
|
||||
self.assertTrue(f._has_changed(True, None))
|
||||
self.assertTrue(f._has_changed(True, False))
|
||||
self.assertTrue(f.has_changed(False, None))
|
||||
self.assertTrue(f.has_changed(None, False))
|
||||
self.assertFalse(f.has_changed(None, None))
|
||||
self.assertFalse(f.has_changed(False, False))
|
||||
self.assertTrue(f.has_changed(True, False))
|
||||
self.assertTrue(f.has_changed(True, None))
|
||||
self.assertTrue(f.has_changed(True, False))
|
||||
|
||||
# MultipleChoiceField #########################################################
|
||||
|
||||
|
@ -1116,13 +1120,13 @@ class FieldsTests(SimpleTestCase):
|
|||
|
||||
def test_multiplechoicefield_changed(self):
|
||||
f = MultipleChoiceField(choices=[('1', 'One'), ('2', 'Two'), ('3', 'Three')])
|
||||
self.assertFalse(f._has_changed(None, None))
|
||||
self.assertFalse(f._has_changed([], None))
|
||||
self.assertTrue(f._has_changed(None, ['1']))
|
||||
self.assertFalse(f._has_changed([1, 2], ['1', '2']))
|
||||
self.assertFalse(f._has_changed([2, 1], ['1', '2']))
|
||||
self.assertTrue(f._has_changed([1, 2], ['1']))
|
||||
self.assertTrue(f._has_changed([1, 2], ['1', '3']))
|
||||
self.assertFalse(f.has_changed(None, None))
|
||||
self.assertFalse(f.has_changed([], None))
|
||||
self.assertTrue(f.has_changed(None, ['1']))
|
||||
self.assertFalse(f.has_changed([1, 2], ['1', '2']))
|
||||
self.assertFalse(f.has_changed([2, 1], ['1', '2']))
|
||||
self.assertTrue(f.has_changed([1, 2], ['1']))
|
||||
self.assertTrue(f.has_changed([1, 2], ['1', '3']))
|
||||
|
||||
# TypedMultipleChoiceField ############################################################
|
||||
# TypedMultipleChoiceField is just like MultipleChoiceField, except that coerced types
|
||||
|
@ -1169,7 +1173,7 @@ class FieldsTests(SimpleTestCase):
|
|||
def test_typedmultiplechoicefield_has_changed(self):
|
||||
# has_changed should not trigger required validation
|
||||
f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=True)
|
||||
self.assertFalse(f._has_changed(None, ''))
|
||||
self.assertFalse(f.has_changed(None, ''))
|
||||
|
||||
def test_typedmultiplechoicefield_special_coerce(self):
|
||||
"""
|
||||
|
@ -1334,7 +1338,7 @@ class FieldsTests(SimpleTestCase):
|
|||
|
||||
def test_splitdatetimefield_changed(self):
|
||||
f = SplitDateTimeField(input_date_formats=['%d/%m/%Y'])
|
||||
self.assertFalse(f._has_changed(['11/01/2012', '09:18:15'], ['11/01/2012', '09:18:15']))
|
||||
self.assertTrue(f._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['2008-05-06', '12:40:00']))
|
||||
self.assertFalse(f._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['06/05/2008', '12:40']))
|
||||
self.assertTrue(f._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['06/05/2008', '12:41']))
|
||||
self.assertFalse(f.has_changed(['11/01/2012', '09:18:15'], ['11/01/2012', '09:18:15']))
|
||||
self.assertTrue(f.has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['2008-05-06', '12:40:00']))
|
||||
self.assertFalse(f.has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['06/05/2008', '12:40']))
|
||||
self.assertTrue(f.has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), ['06/05/2008', '12:41']))
|
||||
|
|
Loading…
Reference in New Issue