如何限制带有 FileFields 的 ModelForms 文件上传的文件类型?

问题描述 投票:0回答:8

我的目标是将 Django ModelForm 上的 FileField 限制为 PDF 和 Word 文档。我在谷歌上搜索到的答案都涉及创建一个单独的文件处理程序,但我不确定如何在 ModelForm 的上下文中执行此操作。我可以使用 settings.py 中的设置来限制上传文件类型吗?

django django-forms django-file-upload
8个回答
52
投票

创建一个验证方法,例如:

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!')

35
投票

使用文件名扩展名进行验证并不是一种一致的方法。例如,我可以将图片.jpg 重命名为图片.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])

16
投票

更简单的方法如下在您的表格中

file = forms.FileField(widget=forms.FileInput(attrs={'accept':'application/pdf'}))

8
投票

Django 自 1.11 起就有一个 FileExtensionValidator 用于此目的:

from django.core.validators import FileExtensionValidator

class SomeDocument(Model):
    document = models.FileField(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 库之类的东西可以帮助您确定实际的文件类型。


6
投票

为了更通用的用途,我编写了一个小类

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'])])

5
投票

我使用了一些类似的东西(注意,“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 模型的验证器列表中。


1
投票

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/


-1
投票

例如,Django-Filebrowser 采用在 settings.py 中创建可接受的文件扩展名列表的方法。

希望对您有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.