【问题标题】:Allow SVG files to be uploaded to ImageField via Django admin允许通过 Django admin 将 SVG 文件上传到 ImageField
【发布时间】:2016-10-26 15:33:48
【问题描述】:

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

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

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

【问题讨论】:

  • ImageField 和 FileField 之间的基本区别在于,首先使用 Pillow 检查文件是否为图像,并提供一些可能与您无关的属性(高度、宽度)。 FileField 不能满足您的要求吗? github.com/django/django/blob/master/django/db/models/fields/…
  • @Wtower 我希望它不允许非图像格式通过。
  • 尽管 OP 不担心有害文件,但最好记住制作有害的 svg 是多么容易。例如,如果您使用xml.etreebillion laughs 使您的服务器崩溃(正如此处的许多答案所建议的那样)。

标签: python django svg django-forms


【解决方案1】:

我从未使用过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")

【讨论】:

  • 感谢您的回答,这几乎就是 SVGAndImageFormField 所做的。
  • 第二个valid 参数是什么?它在旧 Django 版本中使用过吗? (它在 1.11 上爆发了)
【解决方案2】:

事实证明,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,这也有效。 models.ImageFieldheightwidth 属性仍适用于光栅图像类型,对于 SVG 将设置为 None

【讨论】:

  • 可能对某人有帮助。表单 meta 中的 field_classes 仅在 django 1.9 中可用,对于使用旧版本的用户,您需要在表单中显式定义字段。
  • 不工作了,原因和stackoverflow.com/questions/38006200/…一样
【解决方案3】:

这是一个作为简单模型字段工作的解决方案,您可以用它代替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 并且很高兴:)

【讨论】:

  • 不再使用 Django 2.0。即使 svg 验证通过并且 to_python 返回文件,表单仍然会引发错误“文件扩展名 'svg' is not allowed. Allowed extensions are: 'blp, bmp, bufr, cur, pcx, dcx, dds, ps , eps, 适合, 适合, fli, flc, ftc, ftu, gbr, gif, grib, h5, hdf, png, jp2, j2k, jpc, jpf, jpx, j2c, icns, ico, im, iim, tif, tiff , jfif, jpe, jpg, jpeg, mpg, mpeg, mpo, msp, palm, pcd, pdf, pxr, pbm, pgm, ppm, psd, bw, rgb, rgba, sgi, ras, tga, webp, wmf, emf , xbm, xpm'。”
  • 我必须以与 SVGAndImageFieldForm.run_python 相同的方式覆盖 SVGAndImageFieldForm.run_validators 才能使其正常工作
  • @Fandekasp,你能否澄清一下你是否能够让它在 Django 2.0 上工作? SVGAndImageFieldForm.run_validators在哪里?
  • class SVGAndImageFieldForm(forms.ImageField): default_validators = [FileExtensionValidator(allowed_extensions=get_available_image_extensions() + ["svg"])]
【解决方案4】:

如 cmets 中所述,SVGAndImageFormField 的验证将失败,因为使用 django.core.validators.validate_image_file_extension 检查扩展,这是 ImageField 的默认验证器。

解决方法是创建一个自定义验证器,将 "svg" 添加到接受的扩展中。

已编辑:感谢@Ilya Semenov 的评论

from django.core.validators import (
    get_available_image_extensions,
    FileExtensionValidator,
)


def validate_image_and_svg_file_extension(value):
    allowed_extensions = get_available_image_extensions() + ["svg"]
    return FileExtensionValidator(allowed_extensions=allowed_extensions)(value)

然后,覆盖SvgAndImageFormField 中的default_validators 属性:

class SVGAndImageFormField(DjangoImageField):
    default_validators = [validate_image_and_svg_file_extension]
# ...

【讨论】:

  • 此代码不正确,因为它通过引用就地修改了get_available_image_extensions() 的返回值。根据底层实现,这可能会破坏事情。你应该做get_available_image_extensions() + ["svg"]
【解决方案5】:
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

它的工作

【讨论】:

    猜你喜欢
    • 2012-10-12
    • 2010-11-21
    • 2011-04-28
    • 2012-12-11
    • 1970-01-01
    • 2020-12-10
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多