允许SVG文件通过Django admin上传到ImageField

时间:2016-06-24 05:31:12

标签: python django svg django-forms

我切换到SVG图像来代表我的电子商务平台上的类别。我之前在Category模型中使用models.ImageField来存储图像,但forms.ImageField验证无法处理基于矢量的图像(因此拒绝它)。

我不需要对有害文件进行彻底验证,因为所有上传都将通过Django Admin完成。看起来我必须在我的模型中切换到models.FileField,但我确实希望警告不要上传无效图像。

Nick Khlestov在SVGAndImageFormField上写了django-rest-framework's ImageField(在文章中找到来源,我没有足够的声誉发布更多链接)。我如何使用这个解决方案而不是Django的ImageField(而不是DRF)?

5 个答案:

答案 0 :(得分:5)

我从未使用SVGAndImageFormField所以我无法对此发表评论。就个人而言,我会选择FileField的简单应用,但这显然取决于项目要求。我将在下面进行扩展:

如评论中所述,ImageField和FileField之间的基本区别在于,第一个使用Pillow检查文件是否为图像:

  

继承FileField中的所有属性和方法,但也验证上传的对象是有效图像。

参考:Django docsDjango source code

它还提供了一些可能与SVG情况无关的属性(高度,宽度)。

因此,模型字段可以是:

    svg = models.FileField(upload_to=..., validators=[validate_svg])

您可以使用相关问题中提供的is_svg之类的功能:

How can I say a file is SVG without using a magic number?

然后是一个验证SVG的函数:

def validate_svg(file, valid):
    if not is_svg(file):
        raise ValidationError("File not svg")

答案 1 :(得分:4)

事实证明,SVGAndImageFormField与DRF ImageField没有任何依赖关系,只会增加django.forms.ImageField完成的验证。

因此,要接受Django Admin中的SVG,我将模型的ImageField更改为FileField指定了覆盖,如下所示:

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        exclude = []
        field_classes = {
            'image_field': SVGAndImageFormField,
        }

class MyModelAdmin(admin.ModelAdmin):
    form = MyModelForm

admin.site.register(MyModel, MyModelAdmin)

它现在接受所有以前的图像格式以及SVG。

编辑:发现即使您没有从models.ImageField切换到models.FileField,这仍然有效。 height的{​​{1}}和width属性仍然适用于栅格图像类型,并且对于SVG将设置为models.ImageField

答案 2 :(得分:1)

这是一个解决方案,可以作为一个简单的模型字段,而不是models.ImageField

class Icon(models.Model):
    image_file = SVGAndImageField()

您需要在代码中的某处定义以下类和函数:

from django.db import models

class SVGAndImageField(models.ImageField):
    def formfield(self, **kwargs):
        defaults = {'form_class': SVGAndImageFieldForm}
        defaults.update(kwargs)
        return super().formfield(**defaults)

以下是SVGAndImageFieldForm的样子:

from django import forms
from django.core.exceptions import ValidationError

class SVGAndImageFieldForm(forms.ImageField):
    def to_python(self, data):
        try:
            f = super().to_python(data)
        except ValidationError:
            return validate_svg(data)

        return f

我从其他解决方案中获取的函数validate_svg

import xml.etree.cElementTree as et

def validate_svg(f):
    # Find "start" word in file and get "tag" from there
    f.seek(0)
    tag = None
    try:
        for event, el in et.iterparse(f, ('start',)):
            tag = el.tag
            break
    except et.ParseError:
        pass

    # Check that this "tag" is correct
    if tag != '{http://www.w3.org/2000/svg}svg':
        raise ValidationError('Uploaded file is not an image or SVG file.')

    # Do not forget to "reset" file
    f.seek(0)

    return f

此外,如果您只想使用 SVG文件模型字段 - 您可以更简单地使用它。

只需创建继承自models.FileField的类,然后使用__init__方法将validate_svg函数添加到kwargs['validators']

或者只是将此验证器添加到models.FileField并感到高兴:)

答案 3 :(得分:0)

from django.forms import ModelForm, FileField

class TemplatesModelForm(ModelForm):
    class Meta:
        model = Templates
        exclude = []
        field_classes = {
            'image': FileField,
        }

@admin.register(Templates)
class TemplatesAdmin(admin.ModelAdmin):
    form = TemplatesModelForm

它的工作

答案 4 :(得分:0)

如评论中所述,对SVGAndImageFormField的验证将失败,因为扩展名是使用django.core.validators.validate_image_file_extension检查的,ImageField"svg"的默认验证器。

一种解决方法是创建一个自定义验证器,将from django.core.validators import ( get_available_image_extensions, FileExtensionValidator, ) def validate_image_and_svg_file_extension(value): allowed_extensions = get_available_image_extensions() allowed_extensions.append("svg") return FileExtensionValidator(allowed_extensions=allowed_extensions)(value) 添加到接受的扩展名中。

default_validators

然后,覆盖SvgAndImageFormField中的class SVGAndImageFormField(DjangoImageField): default_validators = [validate_image_and_svg_file_extension] # ... 属性:

[Wed Apr 10 15:04:57.629696 2019] [proxy_fcgi:error] [pid 15:tid 139956064347904] [client 172.17.0.18:52820] AH01071: Got error 'Primary script unknown\n', referer: http://local.test.com