This is a beginner question. I'm working on a website that allows users to upload a video to a project model (via ModelForm) and I want to validate this file correctly. I originally declared the field in this way:
from django.db import models
from django.core.validators import FileExtensionValidator
def user_directory_path(instance, filename):
"""
Code to return the path
"""
class Project(models.Model):
"""
"""
# ...Some model fields...
# Right now I'm only validating and testing with .mp4 files.
video_file = models.FileField(
upload_to=user_directory_path,
validators=[FileExtensionValidator(allowed_extensions=['mp4'])]
)
But I read in several places that it was better to use libmagic
to check the file's magic numbers and make sure its contents match the extension and the MIME type. I'm very new to this, so I might get some things wrong.
I followed the validators reference to write a custom validator that uses magic
. The documentation also talks about "a class with a __cal__()
method," and the most upvoted answer here uses a class-based validator. The documentation says that this can be done "for more complex or configurable validators," but I haven't understood what would be a specific example of that and if my function-based validator is just enough for what I'm trying to do. I think it is, but I don't have experience to be sure.
This is what I have.
models.py
from django.db import models
from .validators import validate_media_file
def user_directory_path(instance, filename):
"""
Code to return the path
"""
class Project(models.Model):
"""
"""
# ...Some model fields...
# Right now I'm only validating and testing with .mp4 files.
video_file = models.FileField(
upload_to=user_directory_path,
validators=[validate_media_file]
)
validators.py (basically taken from the example in the documentation)
import os
import magic
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
def validate_media_file(value):
"""
"""
# Upper case to then see if it's in magic.from_buffer() output
file_extension = os.path.splitext(value.name)[1].upper()[1:]
# A list because later I will validate other formats
if file_extension not in ['MP4']:
raise ValidationError(
_('File %(value)s does not contain a valid extension'),
params={'value': value},
)
elif file_extension not in magic.from_buffer(value.read()):
raise ValidationError(
_(<appropriate error message>),
params={'value': value},
)
The migration worked with this. I also tested it with a plain text file with a .mp4 extension and then with a different file (and extension) and it works. However, I'd like to know if I'm missing something by using this instead of a class-based validator, and also, as the title says, when should I use one because I might come across another situation in which I'd need to know it.
I know I haven't included the MIME type; I can do it later.
As an additional question, what would be an appropriate error message for when the output from magic.from_buffer()
does not match the extension and/or MIME type? I thought about something saying the "file is corrupt," but I'm not sure. Actually, is this the output that's directly based on the magic numbers?