Django在被stdimage

时间:2015-09-25 13:46:42

标签: python django

我正在使用django-stdimage来创建图像的变体。

class Photo(models.Model):
    photo = StdImageField(upload_to='photos', verbose_name=_("photo"),
                      variations={'large': (600, 600), 'thumbnail': (100, 100)}

StdImageField对图片进行自己的操作,继承ImageField并拥有attr_class = StdImageFieldFile

StdImageFieldFile执行实际的保存操作

class StdImageFieldFile(ImageFieldFile):
    """
    Like ImageFieldFile but handles variations.
    """

    def save(self, name, content, save=True):
        super(StdImageFieldFile, self).save(name, content, save)
        render_variations = self.field.render_variations
        if callable(render_variations):
            render_variations = render_variations(
                file_name=self.name,
                variations=self.field.variations,
                storage=self.storage,
            )
        if not isinstance(render_variations, bool):
            msg = (
                '"render_variations" callable expects a boolean return value,'
                ' but got %s'
                ) % type(render_variations)
            raise TypeError(msg)
        if render_variations:
            self.render_variations()

但是,我想在StdImageFieldFile(旋转)之前对图像进行一些操作。

所以我创建了自定义字段,以便在将图像传递给stdimage之前捕获图像

class Rotate(ImageFieldFile):
    def save(self, name, content, save=True):
        save = False

        return super(Rotate, self).save(name, content, save)

class StdImageFieldFileRotateMixin(Rotate, StdImageFieldFile):
    pass

class StdImageFieldRotate(StdImageField):
    attr_class = StdImageFieldFileRotateMixin

我在content类的Rotate属性中有图像,我可以使用PIL操作图像,但在完成之后,我不知道如何将此图像分配回内容属性。它似乎是在较低级别生成的。有没有一种方法来生成这个content属性,然后MRO将处理其余的(即将其传递给StdImageFieldFile,它将完成其余的工作)?

1 个答案:

答案 0 :(得分:1)

您可以将callable传递给render_variations类的参数StdImageField并在那里预处理图像。

例如,假设您想要节省磁盘空间并将原始图像调整为较小的版本,只将较小的图像与django-stdimage创建的变体一起使用。你可以写一个这样的函数:

from io import BytesIO
from PIL import Image

from django.core.files.base import ContentFile

from stdimage.utils import render_variations

def preprocess(file_name, variations, storage):
    with storage.open(file_name) as f:
        with Image.open(f) as image:
            file_format = image.format

            # resize to a maximum of 1000x1000 keeping aspect ratio
            image.thumbnail((1000, 1000), resample=Image.ANTIALIAS)

            with BytesIO() as file_buffer:
                image.save(file_buffer, file_format)
                f = ContentFile(file_buffer.getvalue())
                # delete the original big image
                storage.delete(file_name)
                # save the resized version with the same filename and format
                storage.save(file_name, f)

    # render stdimage variations
    render_variations(file_name, variations, replace=True, storage=storage)

    return False  # prevent default rendering

然后,您可以将该函数传递给render_variations类的StdImageField参数,如下所示:

class Photo(models.Model):
    photo = StdImageField(upload_to='photos', verbose_name=_("photo"),
                  variations={'large': (600, 600), 'thumbnail': (100, 100)},
                  render_variations=preprocess)

这项技术的灵感来自于Github上django-stdimage项目自述文件中的Async image processing example

此外,在preprocess函数中处理图像的方式受到stdimage.models.StdImageFieldFile.render_variation方法的启发,并使用Django Storage对象来处理图像文件的不同存储类型(想想Amazon S3)。