Как ограничить размеры картинок при загрузке в поле ImageField Django

Django хранит картинки в полях типа ImageField. В стандартном виде это поле не ограничивает размер загружаемого изображения. Поэтому разработчики применяют сторонние и самописные решения, чтобы не хранить на серверах гигабайты фото в высоком разрешении.

Установим минимальный размер картинки в поле ImageField

Определим метод clean модели и в нем проверим поле картинки ImageField. Сейчас проверим только минимальные размеры изображения. Вот как это сделать:

from django.core.exceptions import ValidationError

class Image(models.Model):
    """Модель картинки."""

    image = models.ImageField('Картинка', upload_to='images')

    def clean(self):
        if self.image:
            if self.image.width < 600 or self.image.height < 600:
                raise ValidationError(
                    {'image': 'Минимальный размер картинки 600х600 пикселей. '}
                )
        return super().clean()

    def save(self, force_insert, force_update, using, update_fields):
        self.full_clean()
        return super().save(force_insert, force_update, using, update_fields)

Метод save переопределяем, чтобы вызывать методы очистки при сохранении объектов модели вне админки. Если работаем с моделями только через административный раздел Django, метод save можно не переопределять.

Оформим код проверки размеров в виде функции, чтобы использовать его в других моделях с картинками без повторов.

def image_size_validator_factory(min_w=600, min_h=600, field_name='image'):
    def validator(image):
        if image.width < min_w or image.height < min_h:
            raise ValidationError(
                {field_name: f'Размер картинки от {min_w}х{min_h} пикселей. '}
            )

    return validator

Теперь перепишем метод clean модели с картинкой.

...
def clean(self):
    if self.image:
        image_size_validator_factory()(self.image)
    return super().clean()
...

Ограничим максимальное разрешение картинки в модели Django

Большую картинку можно пережать в картинку поменьше. Сделаем это на стороне сервера, чтобы не раздражать пользователя лишними действиями при работе с сайтом.

Установим пакет django-resized. Эта батарейка Django позволяет контролировать загружаемые изображения: сжимать до нужных размеров, преобразовывать к нужному формату, нормализовать ориентацию и очищать метаданные.

Сначала в settings.py проекта пропишем настройки для django-resized:

DJANGORESIZED_DEFAULT_SIZE = [1920, 1920]
DJANGORESIZED_DEFAULT_SCALE = 1
DJANGORESIZED_DEFAULT_QUALITY = 86
DJANGORESIZED_DEFAULT_KEEP_META = True
DJANGORESIZED_DEFAULT_FORCE_FORMAT = 'JPEG'
DJANGORESIZED_DEFAULT_FORMAT_EXTENSIONS = {'JPEG': ".jpg"}
DJANGORESIZED_DEFAULT_NORMALIZE_ROTATION = True

Далее в модели изменим класс поля ImageField на ResizedImageField. В поле можно определить размер, до которого нужно сжимать загружаемые картинки. Сжатие работает по "большей стороне" и сохраняет соотношение сторон. Если не определить параметр size, то сработают дефолтные настройки из settings.py. Больше параметров ищите на странице документации django-resized.

from django_resized import ResizedImageField

class Image(models.Model):
    """Модель картинки."""

    image = ResizedImageField('Картинка', upload_to='images', size=[1200, 1200])
    ...

Теперь загрузить получится только изображение от 600х600 пикселей, а большие фото админка автоматически сожмет до размера 1200х1200 пикселей. Также сайт преобразует картинки в JPEG формат.

Разделы: Django Python

Автор: stan / 10 Мар 2023