使用Django的FileField跟踪图像类型

时间:2014-06-04 23:45:20

标签: python django upload pillow

我的模型有FileField接受图片。我用Pillow验证它们并强制执行图像格式,但我也想用适当的扩展名保存图像文件。例如,如果我上传名为foo.gif的JPEG图像,我想将其保存为000.jpg,其中数字是模型中的另一个字段。

我在哪里存储图像格式,以便稍后可以在我的upload_to callable中重复使用它来生成上传路径?

class ImageField(models.FileField):
    default_error_messages = {
        'invalid_image': "Upload a valid image. The file you uploaded was either not an image or a corrupted image.",
        'invalid_format': "Upload an image in a valid format. The supported formats are JPEG and PNG.",
    }

    def to_python(self, data):
        f = super(ImageField, self).to_python(data)

        if f is None:
            return None

        if hasattr(data, 'temporary_file_path'):
            file = data.temporary_file_path()
        elif hasattr(data, 'read'):
            file = BytesIO(data.read())
        else:
            file = BytesIO(data['content'])

        try:
            image = Image.open(file)
            image.verify()

            if image.format not in {'PNG', 'JPEG'}:
                raise ValidationError(self.error_messages['invalid_format'], code='invalid_format')
        except Exception:
            raise ValidationError(self.error_messages['invalid_image'], code='invalid_image')

        if hasattr(f, 'seek') and callable(f.seek):
            f.seek(0)

        return f

class Image(models.Model):
    number = models.PositiveSmallIntegerField()
    image = ImageField(upload_to=self.upload_path)

    def upload_path(self, instance, upload_filename):
        return ???

3 个答案:

答案 0 :(得分:1)

首先,您粘贴在上面的代码片段来自 django forms 。请注意,Django有两种字段类:模型字段和表单字段。他们的名字通常是相同的,但实施和目的完全不同。在您的示例中,您尝试使用从自定义模型字段中的表单字段复制的代码。

真正感兴趣的代码在这里:https://github.com/django/django/blob/master/django/core/files/images.py - 这是提取图像尺寸的片段。然后在此处使用Mixin类 ImageFile ImageFieldFile 中的https://github.com/django/django/blob/master/django/db/models/fields/files.py。然后将后者用作 ImageField 模型字段类中的属性类。

所以,你所需要的只是:

  1. 复制django/core/files/images.py并对其进行修改,不仅可以提取维度,还可以提取文件类型(例如,您将此名称命名为ImageFileWithType)。
  2. 通过提供自定义mixin覆盖ImageFieldFile;
  3. 覆盖ImageField类,替换其中的属性类。
  4. from your_custom_fields_module import ImageFileWithType
    from django.db.models.fields import ImageFieldFile, ImageField
    
    
    class ImageFieldFileWithType(ImageFileWithType, ImageFieldFile):
        """As `ImageFileWithType` mixin is at the beginning,
        it overrides default mixin"""
        pass
    
    
    class ImageFieldWithType(ImageField):
        attr_class = ImageFieldFileWithType
    

    然后在模型中使用此模型字段!请注意,如果您使用的是南迁移,则需要特别注意处理自定义模型字段。

    希望,这有帮助!

答案 1 :(得分:0)

真的,看起来你甚至不需要使用Pillow(除非你没有发布的项目更多)。您所需要的只是定义storage system然后 - 在检索时 - 访问FileField的{​​{3}}。我建议使用这种方法,因为文件及其格式的99%的时间信息是通过使用文件对象本身,从文件名确定信息,或者(在极少数情况下)使用命令行魔术来找到的。 subprocess

无论如何,这是一个存储示例:

from django.core.files.storage import Storage

class MyStorage(Storage):
    ...
    def get_valid_name(name):
        return name.replace('gif', 'jpg')
    ...

这就是你可能会有点困惑的地方。 Django拥有自己的存储引擎,可以自动将文件写入MEDIA_ROOT目录。因此,您的代码段

def upload_path(self, instance, upload_filename):
    return ???

是多余的(如果您定义自己的存储,那就是;))现在它更有意义吗? django.core.files.storage.Storage可以帮助您。

答案 2 :(得分:0)

啊,我明白了。抱歉不理解。

Pillow提供Image Class,其中包含有关图像的信息。 Image.format可能是您想要的财产。

我甚至对自己进行了一些测试,看看扩展是否重要:

user@host:~/Images$ cp myimage.jpg myimage.gif
user@host:~/Images$ cp myimage.jpg myimage.txt  # Extreme test!
user@host:~/Images$ python
Python 2.7.4 (default, Apr 19 2013, 18:28:01) 
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from PIL import Image
>>> im = Image.open('/home/user/Images/myimage.jpg')
>>> im.format
'JPEG'
>>> im = Image.open('/home/user/Images/myimage.gif')
>>> im.format
'JPEG'

不,不重要。看起来不错!的