Процессор Django ImageKit: использовать размеры, хранящиеся в базе данных

У меня есть модель, где ширина эскиза варьируется между родительскими объектами (ForeignKey). Я должен быть в состоянии передать эту информацию процессорам imagekit. Вот что у меня есть:

class Wall(models.Model):
    #...
    width = models.SmallIntegerField(null=True, blank=True)
    #...


class Poster(models.Model):
    wall = models.ForeignKey(Wall, related_name='posters')
    #...
    original_image = models.ImageField(upload_to=upload_image_to)

    def __init__(self, *args, **kwargs):
        self.thumbnail = ImageSpecField([
                Adjust(contrast=1.2, sharpness=1.1),
                SmartResize(height=163, width=self.wall.width)
            ],
            image_field='original_image', format='PNG'
        )

        super(Poster, self).__init__(*args, **kwargs)
    #...

Но если я сделаю это, ничего не произойдет, даже не будет создан URL-адрес эскиза.

И следующее это приведет к этому исключению:
AttributeError: объект ForeignKey не имеет атрибута width

class Poster(models.Model):
    wall = models.ForeignKey(Wall, related_name='posters')
    #...
    original_image = models.ImageField(upload_to=upload_image_to)

    thumbnail = ImageSpecField([
            Adjust(contrast=1.2, sharpness=1.1),
            SmartResize(height=163, width=wall.width)
        ],
        image_field='original_image', format='PNG'
    )
    #...

2 ответа

Решение

Вы не можете ссылаться на значения экземпляра в определении модели. Вот тут-то и стало немного сложно с Джанго; Модели являются декларацией того, как будет выглядеть экземпляр при создании экземпляра, поэтому взаимозависимые поля должны ссылаться на другие поля по имени, например image_field='original_image' Спецификация.

Глядя на источник для imagekit, вы можете увидеть, что processors может принимать либо список статических процессоров, которые нужно применить, либо он требует вызова, который должен возвращать список процессоров, которые будут применены во время генерации. Поскольку вы хотите, чтобы поколение изменялось во время выполнения в зависимости от ширины, вы можете использовать это в своих интересах.

processors вызываемый вызывается с экземпляром, на котором thumbnail Появится поле, которое затем позволяет выполнить поиск по ширине.

def thumbnail_processors(instance, file):
    # Dynamic width lookup.
    width = instance.wall.width
    return [
        Adjust(contrast=1.2, sharpness=1.1),
        SmartResize(width=width, height=163),
        ]


class Poster(models.Model):
    wall = models.ForeignKey(Wall, related_name='posters')
    #...
    original_image = models.ImageField(upload_to=upload_image_to)

    thumbnail = ImageSpecField(
        processors=thumbnail_processors,
        image_field='original_image', format='PNG'
    )
    #...

Теперь, когда вы получаете доступ к полю, thumbnail_processors будет вызван для получения списка процессоров во время выполнения, а не при объявлении модели. width получен из вашего внешнего ключа и произведено соответствующее изменение размера.

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

Ответ № 1 не соответствует текущей версии django-imagekit (3.0.3). И я нашел официальное решение. Пожалуйста, обратитесь http://django-imagekit.readthedocs.org/en/latest/advanced_usage.html

Другие вопросы по тегам