1. Евгений

    16.04.2009

    0 ↑
    0 ↓
    Тренируюсь с загрузкой изображений. В форме присутствует лишь одно поле - avatar класса ImageField. Хочу до момента сохранения загруженного изображения изменить его размер. Пытаюсь делать это в функции clean_avatar:
        def clean_avatar(self):
    image = self.cleaned_data['avatar']

    from PIL import Image
    from StringIO import StringIO

    if hasattr(image, 'temporary_file_path'):
    f = image.temporary_file_path()
    else:
    if hasattr(image, 'read'):
    f = StringIO(image.read())
    else:
    f = StringIO(image['content'])

    img = Image.open(f)
    img.thumbnail((48, 48), Image.ANTIALIAS)
    img.save(image, 'JPEG')

    return image
    Все бы хорошо, но вылезает ошибка: 'InMemoryUploadedFile' object has no attribute '_mode'.

    Полазил по исходникам и обнаружил, что атрибут mode не присваивается в момент создания объекта InMemoryUploadedFile. Таким образом простейший image.write не работает для объектов данного класса. Что делать в таком случае? Есть варианты обхода?

    Большая просьба не предлагать перенести процесс изменения размера изображения в метод save модели. Представьте, что нагрузка на машину настолько высока, что нельзя позволить себе сохранение файла, затем его чтение и повторное сохранение уже измененного изображения, и нужно делать все за один подход.
  2. anonymous

    16.04.2009

    0 ↑
    0 ↓
    Что делать в таком случае?
    Цчиться использовать поиск: http://softwaremaniacs.org/forum/django/3133/
  3. Евгений

    17.04.2009

    0 ↑
    0 ↓
    Перед тем как спросить я поискал. Я не из тех, кто вначале загадит форум тьмой вопросов, а в следующую секунду найдет ответ в первом же непрочтенном топике.

    Во-вторых, вы мне дали ссылку на преобразование файла, который уже находится в storage, а я просил указать вариант обработки изображения еще не сохраненного в файл.

    Самостоятельно пришел к следующему варианту.
    class AvatarUploadForm(forms.ModelForm):
    """
    Форма загрузки аватара.

    Преобразует загруженное изображение к размеру AVATAR_SIZE пикселей.
    """
    class Meta:
    model = UserProfile
    fields = ('avatar',)

    def clean_avatar(self):
    image = self.cleaned_data['avatar']

    # Ограничиваем максимальный размер загружаемого изображения величиной
    # максимального загружаемого в память файла
    if image.size > settings.FILE_UPLOAD_MAX_MEMORY_SIZE:
    raise forms.ValidationError(u'Размер изображения превышает %i Мб.' % \
    (settings.FILE_UPLOAD_MAX_MEMORY_SIZE / (1024*1024)))

    from django.core.files.uploadedfile import InMemoryUploadedFile
    from PIL import Image
    from StringIO import StringIO

    f = StringIO(image.read())
    thumb = StringIO()

    img = Image.open(f)

    if img.mode != "RGB":
    img = img.convert("RGB")

    # Метод thumb не используется, т.к. он не увеличивает размер изображения,
    # если оно меньше требуемого
    img = img.crop((0, 0, min(img.size), min(img.size)))
    img = img.resize(settings.AVATAR_SIZE, Image.ANTIALIAS)
    img.save(thumb, 'JPEG')

    return InMemoryUploadedFile(thumb, image.field_name, image.name, image.content_type, thumb.len, image.charset)

    def save(self):
    """
    Удаление старого аватара перед сохранением нового с тем же именем.
    """
    img = self.instance.avatar
    if img.name != img.field.default:
    img.storage.delete(img.name)

    super(AvatarUploadForm, self).save()
    Спасибо за помощь.
  4. ziro

    17.04.2009

    0 ↑
    0 ↓
    Евгений, в целом хорошо, но есть одно замечание:
    методы clean_XXX в моделях используются как правило только для проверки корректности данных. Для обработки данных перед сохранением (или после него) лучше использовать все-таки метод save, для этого он и предназначен.
    С одной стороны работать все равно будет одинаково, но с другой стороны если Ваш код будет в последствии дорабатываться другим программистом он может и не уловить такие тонкости.
  5. Евгений

    17.04.2009

    0 ↑
    0 ↓
    А насколько корректно будет перенести код ресайза изображения в метод save формы, а не модели? И, кстати, на момент вызова form.save загруженный файл находится где: в памяти или уже на диске?
  6. Можно еще глянуть на готовые варианты:
    http://hg.driscolldev.com/django-imagekit/wiki/Home
    http://code.google.com/p/django-photologue/
  7. redbaron

    04.05.2009

    0 ↑
    0 ↓
    Ну и http://code.google.com/p/sorl-thumbnail/ в коллекцию, мне он показался самым удобным.

    Вообще непонятно откуда берется привычка всё писать самому? Это ж типовая задача, для нее 99.99% найдутся готовые решения.
  8. Иван Сагалаев

    04.05.2009

    0 ↑
    0 ↓

    Тут штука в том, что всеразличные ThumbnailField'ы — идеальная задачка для того, кто хочет разобраться, что там как в моделях и формах устроено. Из серии "Everyone has to write his own ThumbnailField and never use it" :-)

  9. redbaron

    04.05.2009

    0 ↑
    0 ↓
    А ну да, это из серии, что каждый админ должен написать свой биллинг :)

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.