我的目标是将 Django ModelForm 上的 FileField 限制为 PDF 和 Word 文档。我用谷歌搜索的答案都是关于创建一个单独的文件处理程序,但我不确定如何在 ModelForm 的上下文中这样做。settings.py 中是否有我可以用来限制上传文件类型的设置?
8 回答
创建一个验证方法,如:
def validate_file_extension(value):
if not value.name.endswith('.pdf'):
raise ValidationError(u'Error message')
并将其包含在 FileField 验证器中,如下所示:
actual_file = models.FileField(upload_to='uploaded_files', validators=[validate_file_extension])
此外,您应该在 setting.py 上创建一个列表并对其进行迭代,而不是手动设置模型允许的扩展。
编辑
过滤多个文件:
def validate_file_extension(value):
import os
ext = os.path.splitext(value.name)[1]
valid_extensions = ['.pdf','.doc','.docx']
if not ext in valid_extensions:
raise ValidationError(u'File not supported!')
使用文件名的扩展名进行验证不是一种一致的方式。例如,我可以将 picture.jpg 重命名为 picture.pdf,并且验证不会引发错误。
更好的方法是检查文件的 content_type。
验证方法
def validate_file_extension(value):
if value.file.content_type != 'application/pdf':
raise ValidationError(u'Error message')
用法
actual_file = models.FileField(upload_to='uploaded_files', validators=[validate_file_extension])
一种更简单的方法是在您的表单中如下所示
file = forms.FileField(widget=forms.FileInput(attrs={'accept':'application/pdf'}))
对于更通用的用途,我编写了一个小类ExtensionValidator
,它扩展了 Django 的内置RegexValidator
. 它接受单个或多个扩展名,以及可选的自定义错误消息。
class ExtensionValidator(RegexValidator):
def __init__(self, extensions, message=None):
if not hasattr(extensions, '__iter__'):
extensions = [extensions]
regex = '\.(%s)$' % '|'.join(extensions)
if message is None:
message = 'File type not supported. Accepted types are: %s.' % ', '.join(extensions)
super(ExtensionValidator, self).__init__(regex, message)
def __call__(self, value):
super(ExtensionValidator, self).__call__(value.name)
现在您可以定义与该字段内联的验证器,例如:
my_file = models.FileField('My file', validators=[ExtensionValidator(['pdf', 'doc', 'docx'])])
我使用这些方面的东西(注意,“pip install filemagic”是必需的......):
import magic
def validate_mime_type(value):
supported_types=['application/pdf',]
with magic.Magic(flags=magic.MAGIC_MIME_TYPE) as m:
mime_type=m.id_buffer(value.file.read(1024))
value.file.seek(0)
if mime_type not in supported_types:
raise ValidationError(u'Unsupported file type.')
您可能还可以将前面的示例合并到其中 - 例如还检查扩展/上传类型(作为主要检查可能比魔术更快。)这仍然不是万无一失的 - 但它更好,因为它更多地依赖于数据在文件中,而不是浏览器提供的标题。
注意:这是一个验证器函数,您希望将其添加到 FileField 模型的验证器列表中。
Django 自 1.11 以来为此目的有一个FileExtensionValidator :
class SomeDocument(Model):
document = models.FileFiled(validators=[
FileExtensionValidator(allowed_extensions=['pdf', 'doc'])])
正如@savp 提到的,您还需要自定义小部件,以便用户首先无法选择不合适的文件:
class SomeDocumentForm(ModelForm):
class Meta:
model = SomeDocument
widgets = {'document': FileInput(attrs={'accept': 'application/pdf,application/msword'})}
fields = '__all__'
您可能需要摆弄accept
才能准确确定您的目的需要哪些 MIME 类型。
正如其他人所提到的,这一切都不会阻止某人通过您的表单重命名badstuff.exe
并innocent.pdf
上传它——您仍然需要安全地处理上传的文件。一旦有了内容, python-magic库之类的东西就可以帮助您确定实际的文件类型。
我发现检查文件类型的最佳方法是检查其内容类型。我还要补充一点,进行类型检查的最佳位置之一是表单验证。我将有一个表单和一个验证如下:
class UploadFileForm(forms.Form):
file = forms.FileField()
def clean_file(self):
data = self.cleaned_data['file']
# check if the content type is what we expect
content_type = data.content_type
if content_type == 'application/pdf':
return data
else:
raise ValidationError(_('Invalid content type'))
以下文档链接可能会有所帮助: https ://docs.djangoproject.com/en/3.1/ref/files/uploads/和https://docs.djangoproject.com/en/3.1/ref/forms/validation/
我通过在 ModelForm 上使用 clean_[your_field] 方法来处理这个问题。您可以在 settings.py 中设置可接受的文件扩展名列表以在您的 clean 方法中进行检查,但是 settings.py 没有内置任何内容来限制上传类型。
例如,Django-Filebrowser 采用在 settings.py 中创建可接受文件扩展名列表的方法。
希望对您有所帮助。