Django - make ModelForm (of an ImageField) accept only certain types of images

django, django-forms, django-models, python, validation

Solution

The `ImageField` validators already verify that the uploaded file is a valid image file supported by Pillow. This is enough to prevent any malicious code injection - the worst that can happen is that someone uploads a file with a different extension than its file format.

I generally don't verify if an uploaded image is indeed the format it portraits to be. As long as it is a valid image file, and the file extension is in my allowed extension, I accept the uploaded file.

You can, however, easily override the `clean_photo` method to determine and verify the actual file type:

from django.utils.image import Image

def clean_photo(self):
    photo = self.cleaned_data.get(['photo'])
    if photo:
        format = Image.open(photo.file).format
        photo.file.seek(0)
        if format in settings.VALID_IMAGE_FILETYPES:
            return photo
    raise forms.ValidationError(...)

The `photo.file.seek(0)` part is important, without it you will run into problems when saving the file!!

Note that `format` is not necessarily equal to the content type or the extension: in the case of `.png` it is `'PNG'`, and in the case of `.jpg` it's `'JPEG'`. I don't know if other formats follow suit; you'll have to test that for yourself.

`Image.open` does not load the entire file content, just the header, so this method is quite fast. Using `timeit` and a random `.png` file, I've run the following code 1,000,000 times:

format = Image.open(g.photo).format
g.photo.seek(0)

The average time for this code was 0.0001 seconds, so yeah, lightning fast.

Problem

I am using Pillow 2.3.0 with Django and I have an ImageField in my models.py like so: ``` class UserImages(models.Model): user = models.ForeignKey(User) photo = models.ImageField(upload_to=get_file_path) ``` and I have a form like so: ``` class UploadImageForm(forms.ModelForm): class Meta: model = UserImages fields = ['photo'] ``` How do I make it so that the UploadImageForm only accepts png's and jpeg's? What I tried is adding this clean method to the form: ``` def clean_photo(self): photo = self.cleaned_data.get('photo', False) if photo: fileType = photo.content_type if fileType in settings.VALID_IMAGE_FILETYPES: #png and jpeg return photo raise forms.ValidationError('FileType not supported: only upload jpegs and pngs.') ``` but according to this documentation (https://docs.djangoproject.com/en/1.5/topics/http/file-uploads/#uploadedfile-objects) it says that "You'll still need to validate that the file contains the content that the content-type header claims – 'trust but verify'." How do I verify that the user did infact upload the file type which he claimed to upload?

Original source