We can also use a class
with a __call__()
method to provide some parameters.
As suggested in Writing validators - Django Docs:
You can also use a class with a __call__()
method for more complex or
configurable validators. RegexValidator
, for example, uses this
technique. If a class-based validator is used in the validators
model
field option, you should make sure it is serializable by the migration
framework by adding deconstruct()
and __eq__()
methods.
Here is a working example:
try:
from collections.abc import Mapping
except ImportError:
from collections import Mapping
from django.utils.translation import ugettext_lazy as _
from django.utils.deconstruct import deconstructible
@deconstructible
class ImageValidator(object):
messages = {
"dimensions": _(
'Allowed dimensions are: %(width)s x %(height)s.'
),
"size": _(
"File is larger than > %(size)skB."
)
}
def __init__(self, size=None, width=None, height=None, messages=None):
self.size = size
self.width = width
self.height = height
if messages is not None and isinstance(messages, Mapping):
self.messages = messages
def __call__(self, value):
# _get_image_dimensions is a method of ImageFile
# https://docs.djangoproject.com/en/1.11/_modules/django/core/files/images/
if self.size is not None and value.size > self.size:
raise ValidationError(
self.messages['size'],
code='invalid_size',
params={
'size': float(self.size)/1024,
'value': value,
}
)
if (self.width is not None and self.height is not None and
(value.width != self.width or value.height != self.height)):
raise ValidationError(
self.messages['dimensions'],
code='invalid_dimensions',
params={
'width': self.width,
'height': self.height,
'value': value,
}
)
def __eq__(self, other):
return (
isinstance(other, self.__class__) and
self.size == other.size and
self.width == other.width and
self.height == other.height
)
And than in model:
class MyModel(models.Model):
...
banner = models.ImageField(
upload_to='uploads/', verbose_name=_("Banner"),
max_length=255, null=True, blank=True,
validators=[ImageValidator(size=256000, width=1140, height=425)],
help_text=_("Please use our recommended dimensions: 1140 x 425 PX, 250 KB MAX"))