In another “note to self” installation in this series, I couldn’t quite find the answer to the question: “how do you validate file extensions in a form upload with Django?”. There’s some other ways with
ModelForms, but I wasn’t using those.
Really, you and I both know that, especially from a security perspective, it’s pretty laughable to only check the file extension. Some other ways we could be accomplishing this:
acceptattribute, which is easy to bypass, but if it’s an honest user’s mistake, it’ll stop them right there
magic, which will do a much better job at detecting what exactly is going on inside that file, for the more security-conscious amongst us
I didn’t opt for either of these because reasons.
Just to quickly cover the basics, if you’re uploading a file in a Django app, you’re probably doing that through a form. And if you’re using a form, you’re probabably using fields. Specifically in the case of the file upload scenario, a
forms.FileField. Subsequently, in your
views.py you’re (hopefully) calling the
form.is_valid() function before doing any processing. That’s where we’ll come in.
If none of the above made sense to you, you should really be reading up on forms in Django.
If it did make sense, you’ll know that when you
call is_valid(), Django first cleans, then validates the form, before passing it onto you. Think of proper type casting, dealing with tokens etc. We’re going to subclass the
FileField class and add our own validation routine to it.
You can do this wherever you like, though it probably makes most sense to do this in
forms.py if this is the extent of your edits. If you’re going further in that, you may want to re-org. Subclassing is standard Python stuff and as simple as this:
from django import forms class CsvFileField(forms.FileField): def validate(self, value): pass
Remember, by the time our custom validation function rolls around, Django will already have applied cleaning and its standard validation routines. That means that the
value argument you see below is an
UploadedFile, you can read more about those here. Long story short, we can grab its
name attribute to check the file extension:
import os class CsvFileField(forms.FileField): def validate(self, value): # First run the parent class' validation routine super().validate(value) # Run our own file extension check file_extension = os.path.splitext(value.name) if file_extension != '.csv': raise ValidationError( ('Invalid file extension'), code='invalid' )
Two things to note:
magiclibrary and do some decent file-extension checking