How to properly scope validator function in parent class for use in child class
I'm trying to use validators, but I'm having a mysterious issue with using validators in a parent/child class.
Right now I've created a base class for all media in my project; all media in this model need a name and a thumbnail:
class BaseMedia(BaseModel):
def validate_thumbnail(image_obj):
validate_image_file_extension(image_obj)
dimensions = [image_obj.width, image_obj.height]
if dimensions[0] != 400 or dimensions[1] != 400:
raise ValidationError(
"Thumbnail dimensions must be 400 x 400; uploaded image is %s x %s"
% (dimensions[0], dimensions[1])
)
media_title = models.CharField(
max_length=25, verbose_name="Title (25 char)", null=True
)
thumbnail = models.ImageField(
null=True,
verbose_name="Thumbnail (400 x 400 px)",
upload_to="microscopy/images/thumbnails",
validators=([validate_thumbnail],),
)
and then I have a child class that inherits and implement the core media itself; for example:
class MicroscopyMedia(BaseMedia):
def validate_image(image_obj):
validate_image_file_extension(image_obj)
dimensions = [image_obj.width, image_obj.height]
if dimensions[0] != 1920 or dimensions[1] != 1070:
raise ValidationError(
"Image dimensions must be 1920 x 1070; uploaded image is %s x %s"
% (dimensions[0], dimensions[1])
)
media = models.ImageField(
null=True,
verbose_name="Media (1920 x 1070 px)",
blank=True,
upload_to="microscopy/images",
validators=[validate_image],
)
The idea being that all models will need a thumbnail, but within the child class I have a specific validator for each individual media type.
However, when I try to make this migration, I get an error:
(env) PS D:\Projects\django-cms\projects\cms> python .\manage.py makemigrations
SystemCheckError: System check identified some issues:
ERRORS:
microscopy.BaseMedia.thumbnail: (fields.E008) All 'validators' must be callable.
HINT: validators[0] ([<function BaseMedia.validate_thumbnail at 0x000001D3193C8A60>]) isn't a function or instance of a validator class.
There seems to be a scoping issue at play here. As defined, validate_thumbnail() is just a regular function within the scope of BaseMedia, and isn't available to MicroscopyMedia. However, I can't make it a member function either, since I can't pass the self parameter.
How should I define this validator function in the parent class so that when the child class calls it, it's a valid function call?
Structure your code in this way:
def validate_thumbnail(image_obj):
validate_image_file_extension(image_obj)
...
class BaseMedia(BaseModel):
...
thumbnail = models.ImageField(
...
validators=([validate_thumbnail],),
)
Similarly, promote def validate_image(): so it's up at module scope,
rather than buried down within a class definition.