Hello, my name is Federico Capoano,
I enjoy developing cutting-edge websites
and working with creative people.
If you are looking for a django "FileField" to use in your model with MAX_UPLOAD_SIZE and CONTENT_TYPE restrictions this post is for you.
Immagine for example that you need to add the possibility to upload a PDF or Zip file in the django admin, but you want to be sure your client will upload only these two filetypes and not other ones.
You might also need the same functionality in the frontend, so why not use "ModelForm" and have just one validation for both applications (admin and frontend)?
With this custom file field you can configure the maximum file size for each FileField independantly from the default behavior of Django.
You can also consider this post as a reference about "How to add a custom filefield with custom behavior to your django application".
Let's consider an example of an existing application named "app".
Create a new file in the directory of the application and call it "extra.py" (you can give it a better name if you can think about one) and paste this code:
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
# models.py in yourproject/app/ from django.db import models from app.extra import ContentTypeRestrictedFileField class MyApp(models.Model): """ My application """ name = models.CharField(max_length=100) description = models.CharField(max_length=250) file = ContentTypeRestrictedFileField( upload_to='pdf', content_types=['application/pdf', 'application/zip'], max_upload_size=5242880 ) created = models.DateTimeField('created', auto_now_add=True) modified = models.DateTimeField('modified', auto_now=True) def __unicode__(self): return self.name
Be sure to use a number for max_upload_size value, if you use a string it won't work.
If you're replacing a FileField you won't need to re-sync your db, while if you are just adding this functionality to your model you will need to re-sync your db:
$ python manage.py syncdb
Even if this method is effective to restrict the content-type, a malicious attacker could still rename a script or whatever to the supported filetype.
To be sure the file being uploaded is really what it claims to be, you have to read it and check it. I even heard about performing a virus check, that wouldn't be a bad Idea either.
Even using this custom filefield a user can upload a huge file to the web server, that will be stored in the temporary directory before being rejected.
Only after the upload of the file is complete the system will return the validation error message and delete the file, this means that any user would be able to stress the server with an upload of a huge file (1 GB) for example.
Fortunately this would never happen because django has a FILE_UPLOAD_MAX_MEMORY_SIZE variable that is set default to 2.5 megabytes.
The difference is that when trying to upload a file that exceeds FILE_UPLOAD_MAX_MEMORY_SIZE the server will just interrupt the connection without returning a nice validation error, which could be potentially confusing for your users.
My suggestion is to set FILE_UPLOAD_MAX_MEMORY_SIZE in your settings.py file so it allows files slightly larger than ContentTypeRestrictedFileField.
So if in your model field you're setting 30 MB as maximum, set FILE_UPLOAD_MAX_MEMORY_SIZE at 45 MB.
This way the system will block users that try to upload files that are much larger than the limit, but still providing a nice validation error to those ones that would be exceeding the limit of few megabytes.
“ Hi Julio! I missed your comment a few years ago but I'm glad you are working with OpenWISP, I'll try to reach you in private :-) ”
By Federico Capoano in A Turning Point in my Life, Community Networks and OpenWISP
“ Great news Aymará! Very happy to know this post has inspired you to experiment :-) ”
By Federico Capoano in First DjangoGirls Rome wrap-up & afterthoughts
“ Hi!! I'm a Django Girls coach too. Here, in Argentina, made just what you suggested, splited the workshop in two days. The experiment went just great! Most of the girls achieved to publish the blog from ground 0. It feels great to be helpfull ... ”
By Aymará in First DjangoGirls Rome wrap-up & afterthoughts
“ Send any question to the interop-dev mailing list or open an issue on github. ”
By Federico in Network Topology Visualizer: django-netjsongraph
“ I have a question about Network Topology Collector, can you brief me pls? ”
By Nasrin Akter in Network Topology Visualizer: django-netjsongraph