我切换到SVG图像来代表我的电子商务平台上的类别。我之前在Category模型中使用models.ImageField
来存储图像,但forms.ImageField
验证无法处理基于矢量的图像(因此拒绝它)。
我不需要对有害文件进行彻底验证,因为所有上传都将通过Django Admin完成。看起来我必须在我的模型中切换到models.FileField
,但我确实希望警告不要上传无效图像。
Nick Khlestov在SVGAndImageFormField上写了django-rest-framework's ImageField(在文章中找到来源,我没有足够的声誉发布更多链接)。我如何使用这个解决方案而不是Django的ImageField(而不是DRF)?
答案 0 :(得分:5)
我从未使用SVGAndImageFormField
所以我无法对此发表评论。就个人而言,我会选择FileField
的简单应用,但这显然取决于项目要求。我将在下面进行扩展:
如评论中所述,ImageField和FileField之间的基本区别在于,第一个使用Pillow检查文件是否为图像:
继承FileField中的所有属性和方法,但也验证上传的对象是有效图像。
参考:Django docs,Django 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