How can I restrict FileField
to only accept a certain type of file (video, audio, pdf, etc.) in an elegant way, server-side?
-
2To get the open dialog to restrict files to certain types client-side, [see this question](http://stackoverflow.com/a/40847649/247696). – Flimm Nov 28 '16 at 15:24
11 Answers
One very easy way is to use a custom validator.
In your app's validators.py
:
def validate_file_extension(value):
import os
from django.core.exceptions import ValidationError
ext = os.path.splitext(value.name)[1] # [0] returns path+filename
valid_extensions = ['.pdf', '.doc', '.docx', '.jpg', '.png', '.xlsx', '.xls']
if not ext.lower() in valid_extensions:
raise ValidationError('Unsupported file extension.')
Then in your models.py
:
from .validators import validate_file_extension
... and use the validator for your form field:
class Document(models.Model):
file = models.FileField(upload_to="documents/%Y/%m/%d", validators=[validate_file_extension])
See also: How to limit file types on file uploads for ModelForms with FileFields?.
Warning
For securing your code execution environment from malicious media files
- Use Exif libraries to properly validate the media files.
- Separate your media files from your application code execution environment
- If possible use solutions like S3, GCS, Minio or anything similar
- When loading media files on client side, use client native methods (for example if you are loading the media files non securely in a browser, it may cause execution of "crafted" JavaScript code)

- 4,178
- 1
- 30
- 47

- 17,240
- 16
- 77
- 97
-
17@dabad using just extension is not good for filevalidation, these kind of answers makes security loopholes; please guys who sees these answers check also CVE related to different formats & validators, take the case of pillow/PIL for example :O – Renjith Thankachan Aug 24 '16 at 06:10
-
1@RenjithThankachan even sometimes images exif data can be modified with arbitrary code but this trick was use to work in the past with PHP apps. – Ammad Khalid Aug 07 '20 at 21:32
-
1One thing we can do is use solutions like S3, Minio to seperate media files of users from application code execution environment @AmmadKhalid – Renjith Thankachan Aug 08 '20 at 02:41
Django in version 1.11
has a newly added FileExtensionValidator
for model fields, the docs is here: https://docs.djangoproject.com/en/dev/ref/validators/#fileextensionvalidator.
An example of how to validate a file extension:
from django.core.validators import FileExtensionValidator
from django.db import models
class MyModel(models.Model):
pdf_file = models.FileField(
upload_to="foo/", validators=[FileExtensionValidator(allowed_extensions=["pdf"])]
)
Note that this method is not safe. Citation from Django docs:
Don’t rely on validation of the file extension to determine a file’s type. Files can be renamed to have any extension no matter what data they contain.
There is also new validate_image_file_extension
for validating image extensions (using Pillow). Another option is to use ImageField
in such a case, which also enforces the check that a valid image has been uploaded.

- 6,033
- 2
- 54
- 66
-
4file got uploaded even though it was not added in `allowed_extensions` for some reason. Using Django Rest Framework. – Vineeth Sai Jun 09 '20 at 08:11
-
2@VineethSai I guess you need to also set the validator inside your serializer, not just in the model. – Rahmat Nazali Salimi Jun 07 '22 at 07:40
A few people have suggested using python-magic to validate that the file actually is of the type you are expecting to receive. This can be incorporated into the validator
suggested in the accepted answer:
import os
import magic
from django.core.exceptions import ValidationError
def validate_is_pdf(file):
valid_mime_types = ['application/pdf']
file_mime_type = magic.from_buffer(file.read(1024), mime=True)
if file_mime_type not in valid_mime_types:
raise ValidationError('Unsupported file type.')
valid_file_extensions = ['.pdf']
ext = os.path.splitext(file.name)[1]
if ext.lower() not in valid_file_extensions:
raise ValidationError('Unacceptable file extension.')
This example only validates a pdf, but any number of mime-types and file extensions can be added to the arrays.
Assuming you saved the above in validators.py
you can incorporate this into your model like so:
from myapp.validators import validate_is_pdf
class PdfFile(models.Model):
file = models.FileField(upload_to='pdfs/', validators=(validate_is_pdf,))

- 507
- 4
- 14
-
8
-
In case it returns "application/octet-stream" for specific known images, you will need to do like this `initial_pos = file.tell()` `file.seek(0)` `mime_type = magic.from_buffer(file.read(2048), mime=True)` `file.seek(initial_pos)` – Hoang HUA Jul 02 '21 at 10:09
-
1I wish Django had an in-the-box solution for confirming that approved mime-types and file extensions match without installing a third-party library. The magic library has different requirements based on your OS which adds more steps to consider during deployment. Great answer, but unfortunate that this is necessary at all. – Jarad Jan 21 '22 at 06:06
-
If it makes sense for it to be included, Django doesn't have it, has no plans of adding it, and has some patronizing word salad about why you don't need it. – blimpse Oct 03 '22 at 20:52
-
After a couple of hours of searching this seems to be the most secure and effective way to ensure the file type. It does include an extra library but what are you gonna do? – Christian Llanos Jun 12 '23 at 18:18
You can use the below to restrict filetypes in your Form
file = forms.FileField(widget=forms.FileInput(attrs={'accept':'application/pdf'}))

- 309
- 3
- 4
There's a Django snippet that does this:
import os
from django import forms
class ExtFileField(forms.FileField):
"""
Same as forms.FileField, but you can specify a file extension whitelist.
>>> from django.core.files.uploadedfile import SimpleUploadedFile
>>>
>>> t = ExtFileField(ext_whitelist=(".pdf", ".txt"))
>>>
>>> t.clean(SimpleUploadedFile('filename.pdf', 'Some File Content'))
>>> t.clean(SimpleUploadedFile('filename.txt', 'Some File Content'))
>>>
>>> t.clean(SimpleUploadedFile('filename.exe', 'Some File Content'))
Traceback (most recent call last):
...
ValidationError: [u'Not allowed filetype!']
"""
def __init__(self, *args, **kwargs):
ext_whitelist = kwargs.pop("ext_whitelist")
self.ext_whitelist = [i.lower() for i in ext_whitelist]
super(ExtFileField, self).__init__(*args, **kwargs)
def clean(self, *args, **kwargs):
data = super(ExtFileField, self).clean(*args, **kwargs)
filename = data.name
ext = os.path.splitext(filename)[1]
ext = ext.lower()
if ext not in self.ext_whitelist:
raise forms.ValidationError("Not allowed filetype!")
#-------------------------------------------------------------------------
if __name__ == "__main__":
import doctest, datetime
doctest.testmod()

- 97,747
- 36
- 197
- 212
-
2This is a filter based on extension, wich is not reliable at all. I was thinking about analysing the file after uploading finishes. – maroxe Sep 08 '10 at 15:48
-
@maroxe Did you find a solution to this? I just have the same issue with determining wether files are audiofiles or not. – marue Feb 17 '11 at 13:42
-
3Try this link: http://nemesisdesign.net/blog/coding/django-filefield-content-type-size-validation/ – PhoebeB Feb 22 '11 at 15:40
-
1@user126795 - that still doesn't actually determine that it's the right type - it appears to just rely on the specified content-type - which may not be reliable. – Dominic Rodger Feb 23 '11 at 10:13
-
2You can use python-magic (wrapper for libmagic) to get the mimetype and then accept/reject based on that. – Daniel Quinn Mar 03 '11 at 20:47
-
A combination of checking the extension, content_type, and using python-magic will handle most circumstances. Note that it is also a good idea to check for the size of the file being uploaded. Also, this snippet has some issues as described here: http://stackoverflow.com/a/12392470/685109 – Esteban Sep 12 '12 at 16:24
First. Create a file named formatChecker.py inside the app where the you have the model that has the FileField that you want to accept a certain file type.
This is your formatChecker.py:
from django.db.models import FileField
from django.forms import forms
from django.template.defaultfilters import filesizeformat
from django.utils.translation import ugettext_lazy as _
class ContentTypeRestrictedFileField(FileField):
"""
Same as FileField, but you can specify:
* content_types - list containing allowed content_types. Example: ['application/pdf', 'image/jpeg']
* max_upload_size - a number indicating the maximum file size allowed for upload.
2.5MB - 2621440
5MB - 5242880
10MB - 10485760
20MB - 20971520
50MB - 5242880
100MB 104857600
250MB - 214958080
500MB - 429916160
"""
def __init__(self, *args, **kwargs):
self.content_types = kwargs.pop("content_types")
self.max_upload_size = kwargs.pop("max_upload_size")
super(ContentTypeRestrictedFileField, self).__init__(*args, **kwargs)
def clean(self, *args, **kwargs):
data = super(ContentTypeRestrictedFileField, self).clean(*args, **kwargs)
file = data.file
try:
content_type = file.content_type
if content_type in self.content_types:
if file._size > self.max_upload_size:
raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s') % (filesizeformat(self.max_upload_size), filesizeformat(file._size)))
else:
raise forms.ValidationError(_('Filetype not supported.'))
except AttributeError:
pass
return data
Second. In your models.py, add this:
from formatChecker import ContentTypeRestrictedFileField
Then instead of using 'FileField', use this 'ContentTypeRestrictedFileField'.
Example:
class Stuff(models.Model):
title = models.CharField(max_length=245)
handout = ContentTypeRestrictedFileField(upload_to='uploads/', content_types=['video/x-msvideo', 'application/pdf', 'video/mp4', 'audio/mpeg', ],max_upload_size=5242880,blank=True, null=True)
Those are the things you have to when you want to only accept a certain file type in FileField.

- 87,323
- 22
- 191
- 272

- 1,658
- 2
- 13
- 8
-
1
-
3Adding this line before the super call: self.widget = ClearableFileInput(attrs={ 'accept': ','.join(self.content_types) }) will make only accepted content types selectable in the modal window. – laffuste Jun 18 '13 at 05:58
-
@laffuste Having upvoted your comment, this doesn't seem to work on Mac (i.e. any files are selectable). Haven't tested under Windows though. – Erve1879 Jul 12 '13 at 08:13
-
1Works in chrome, FF doesn't give a ****. I found out it's better to filter by extension than content type anyway. The later is set almost arbitrarily by your browser + os. – laffuste Jul 12 '13 at 08:38
after I checked the accepted answer, I decided to share a tip based on Django documentation. There is already a validator for use to validate file extension. You don't need to rewrite your own custom function to validate whether your file extension is allowed or not.
https://docs.djangoproject.com/en/3.0/ref/validators/#fileextensionvalidator
Warning
Don’t rely on validation of the file extension to determine a file’s type. Files can be renamed to have any extension no matter what data they contain.

- 91
- 2
- 9
-
This answer just repeats [what was already said 3 years prior](https://stackoverflow.com/posts/43540603/revisions). – Jarad Jan 21 '22 at 06:15
I think you would be best suited using the ExtFileField that Dominic Rodger specified in his answer and python-magic that Daniel Quinn mentioned is the best way to go. If someone is smart enough to change the extension at least you will catch them with the headers.

- 1,341
- 1
- 14
- 24
-
5Since you've determined that this is the best method, why not post some code to demonstrate it to the rest of us!? – Thismatters Dec 11 '17 at 15:50
You can define a list of accepted mime types in settings and then define a validator which uses python-magic to detect the mime-type and raises ValidationError if the mime-type is not accepted. Set that validator on the file form field.
The only problem is that sometimes the mime type is application/octet-stream, which could correspond to different file formats. Did someone of you overcome this issue?

- 1,567
- 2
- 12
- 15
Additionally i Will extend this class with some extra behaviour.
class ContentTypeRestrictedFileField(forms.FileField):
...
widget = None
...
def __init__(self, *args, **kwargs):
...
self.widget = forms.ClearableFileInput(attrs={'accept':kwargs.pop('accept', None)})
super(ContentTypeRestrictedFileField, self).__init__(*args, **kwargs)
When we create instance with param accept=".pdf,.txt", in popup with file structure as a default we will see files with passed extension.

- 305
- 3
- 13
Just a minor tweak to @Thismatters answer since I can't comment. According to the README of python-magic:
recommend using at least the first 2048 bytes, as less can produce incorrect identification
So changing 1024 bytes to 2048 to read the contents of the file and get the mime type base from that can give the most accurate result, hence:
def validate_extension(file):
valid_mime_types = ["application/pdf", "image/jpeg", "image/png", "image/jpg"]
file_mime_type = magic.from_buffer(file.read(2048), mime=True) # Changed this to 1024 to 2048
if file_mime_type not in valid_mime_types:
raise ValidationError("Unsupported file type.")
valid_file_extensions = [".pdf", ".jpeg", ".png", ".jpg"]
ext = os.path.splitext(file.name)[1]
if ext.lower() not in valid_file_extensions:
raise ValidationError("Unacceptable file extension.")

- 191
- 1
- 10