How to limit file types on file uploads for ModelF

2020-01-30 09:10发布

问题:

My goal is to limit a FileField on a Django ModelForm to PDFs and Word Documents. The answers I have googled all deal with creating a separate file handler, but I am not sure how to do so in the context of a ModelForm. Is there a setting in settings.py I may use to limit upload file types?

回答1:

I handle this by using a clean_[your_field] method on a ModelForm. You could set a list of acceptable file extensions in settings.py to check against in your clean method, but there's nothing built-into settings.py to limit upload types.

Django-Filebrowser, for example, takes the approach of creating a list of acceptable file extensions in settings.py.

Hope that helps you out.



回答2:

Create a validation method like:

def validate_file_extension(value):
    if not value.name.endswith('.pdf'):
        raise ValidationError(u'Error message')

and include it on the FileField validators like this:

actual_file = models.FileField(upload_to='uploaded_files', validators=[validate_file_extension])

Also, instead of manually setting which extensions your model allows, you should create a list on your setting.py and iterate over it.

Edit

To filter for multiple files:

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


回答3:

Validating with the extension of a file name is not a consistent way. For example I can rename a picture.jpg into a picture.pdf and the validation won't raise an error.

A better approach is to check the content_type of a file.

Validation Method

def validate_file_extension(value):
    if value.file.content_type != 'application/pdf':
        raise ValidationError(u'Error message')

Usage

actual_file = models.FileField(upload_to='uploaded_files', validators=[validate_file_extension])


回答4:

An easier way of doing it is as below in your Form

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


回答5:

For a more generic use, I wrote a small class ExtensionValidator that extends Django's built-in RegexValidator. It accepts single or multiple extensions, as well as an optional custom error message.

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)

Now you can define a validator inline with the field, e.g.:

my_file = models.FileField('My file', validators=[ExtensionValidator(['pdf', 'doc', 'docx'])])


回答6:

I use something along these lines (note, "pip install filemagic" is required for this...):

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

You could probably also incorporate the previous examples into this - for example also check the extension/uploaded type (which might be faster as a primary check than magic.) This still isn't foolproof - but it's better, since it relies more on data in the file, rather than browser provided headers.

Note: This is a validator function that you'd want to add to the list of validators for the FileField model.