Skip to content

Commit

Permalink
[2.2.x] Fixed #30328 -- Fixed crash of IntegerField.validators when l…
Browse files Browse the repository at this point in the history
…imit_value in a custom validator is callable.

Backport of a14c0fd from master
  • Loading branch information
scottfits authored and felixxm committed Apr 19, 2019
1 parent bb54a2d commit 95811c3
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 27 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,7 @@ answer newbie questions, and generally made Django that much better:
schwank@gmail.com
Scot Hacker <shacker@birdhouse.org>
Scott Barr <scott@divisionbyzero.com.au>
Scott Fitsimones <scott@airgara.ge>
Scott Pashley <github@scottpashley.co.uk>
scott@staplefish.com
Sean Brant
Expand Down
24 changes: 18 additions & 6 deletions django/db/models/fields/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1790,13 +1790,25 @@ def validators(self):
validators_ = super().validators
internal_type = self.get_internal_type()
min_value, max_value = connection.ops.integer_field_range(internal_type)
if (min_value is not None and not
any(isinstance(validator, validators.MinValueValidator) and
validator.limit_value >= min_value for validator in validators_)):
if min_value is not None and not any(
(
isinstance(validator, validators.MinValueValidator) and (
validator.limit_value()
if callable(validator.limit_value)
else validator.limit_value
) >= min_value
) for validator in validators_
):
validators_.append(validators.MinValueValidator(min_value))
if (max_value is not None and not
any(isinstance(validator, validators.MaxValueValidator) and
validator.limit_value <= max_value for validator in validators_)):
if max_value is not None and not any(
(
isinstance(validator, validators.MaxValueValidator) and (
validator.limit_value()
if callable(validator.limit_value)
else validator.limit_value
) <= max_value
) for validator in validators_
):
validators_.append(validators.MaxValueValidator(max_value))
return validators_

Expand Down
4 changes: 4 additions & 0 deletions docs/releases/2.2.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ Bugfixes
:class:`~django.core.paginator.Paginator` crashed when ``object_list`` was
a queryset ordered or aggregated over a nested ``JSONField`` key transform
(:ticket:`30335`).

* Fixed a regression in Django 2.2 where ``IntegerField`` validation of
database limits crashes if ``limit_value`` attribute in a custom validator is
callable (:ticket:`30328`).
46 changes: 25 additions & 21 deletions tests/model_fields/test_integerfield.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,27 +98,31 @@ def test_redundant_backend_range_validators(self):
"""
min_backend_value, max_backend_value = self.backend_range

if min_backend_value is not None:
min_custom_value = min_backend_value + 1
ranged_value_field = self.model._meta.get_field('value').__class__(
validators=[validators.MinValueValidator(min_custom_value)]
)
field_range_message = validators.MinValueValidator.message % {
'limit_value': min_custom_value,
}
with self.assertRaisesMessage(ValidationError, "[%r]" % field_range_message):
ranged_value_field.run_validators(min_backend_value - 1)

if max_backend_value is not None:
max_custom_value = max_backend_value - 1
ranged_value_field = self.model._meta.get_field('value').__class__(
validators=[validators.MaxValueValidator(max_custom_value)]
)
field_range_message = validators.MaxValueValidator.message % {
'limit_value': max_custom_value,
}
with self.assertRaisesMessage(ValidationError, "[%r]" % field_range_message):
ranged_value_field.run_validators(max_backend_value + 1)
for callable_limit in (True, False):
with self.subTest(callable_limit=callable_limit):
if min_backend_value is not None:
min_custom_value = min_backend_value + 1
limit_value = (lambda: min_custom_value) if callable_limit else min_custom_value
ranged_value_field = self.model._meta.get_field('value').__class__(
validators=[validators.MinValueValidator(limit_value)]
)
field_range_message = validators.MinValueValidator.message % {
'limit_value': min_custom_value,
}
with self.assertRaisesMessage(ValidationError, '[%r]' % field_range_message):
ranged_value_field.run_validators(min_backend_value - 1)

if max_backend_value is not None:
max_custom_value = max_backend_value - 1
limit_value = (lambda: max_custom_value) if callable_limit else max_custom_value
ranged_value_field = self.model._meta.get_field('value').__class__(
validators=[validators.MaxValueValidator(limit_value)]
)
field_range_message = validators.MaxValueValidator.message % {
'limit_value': max_custom_value,
}
with self.assertRaisesMessage(ValidationError, '[%r]' % field_range_message):
ranged_value_field.run_validators(max_backend_value + 1)

def test_types(self):
instance = self.model(value=0)
Expand Down

0 comments on commit 95811c3

Please sign in to comment.