Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django's FileField "upload_to" method not called when calling save() with commit=False

Tags:

python

django

Context: I want to take a user uploaded file, attach it to a model, change some properties and then save the object to database.

Here's some relevant code:

models.py

def file_upload_path(instance, filename):
    path = os.path.join('uploaded_files', str(uuid4()))
    return path 

class UploadedFile(models.Model):
    file_object = models.FileField(null=False, blank=False,
                                   upload_to=file_upload_path)

    def __unicode__(self):
        return self.file_object.name

forms.py

class UploadFileForm(forms.ModelForm):
    class Meta:
        model = UploadedFile
        fields = ['file_object']

views.py

def home(request):
...
        if form.is_valid():
            new_file = form.save(commit=True)

            print new_file
...

This code prints exactly what I want, which is the file path after upload_to has been called and the file has been saved to said location (eg. "uploaded_files/b992e44e-6403-4c37-82b4-b3c403d07f79").

Now, with this views.py, things change:

views.py

def home(request):
...
        if form.is_valid():
            new_file = form.save(commit=False)

            print new_file

            # new_file_object = FileObject(os.path.abspath(new_file.file_object.url))
            # new_file.mime_type = new_file_object.get_mime_type()
...

Instead of the path, I get the original filename (eg. "jnes.exe") so I can't process the file so I can get its mime type, for example, and then update the model and save it do the database.

Any ideas what I'm doing wrong? Thanks.

How I got around it:

I do the necessary model edits on the file that's still in memory/temporarily on disk since Django doesn't allow you to directly find a FileField's location until you actually save the new model instance.

forms.py

def save(self, commit=True, *args, **kwargs):
    new_model = super(UploadFileForm, self).save(commit=False)

    file_object = self.cleaned_data['file_object']

    file_info = FileObject(file_object)

    new_model.mime_type = file_info.get_mime_type()
    new_model.sha256 = file_info.get_sha256()
    new_model.md5 = file_info.get_md5()

    if commit:
        new_model.save()

    return new_model

I get the hashes and mime type in FileObject's constructor by reading data from Django's UploadedFile temporary object. (thanks Daniel)

like image 874
Puscasu Emanuel Avatar asked Nov 10 '22 08:11

Puscasu Emanuel


1 Answers

The docs state the following

The file is saved as part of saving the model in the database, so the actual file name used on disk cannot be relied on until after the model has been saved.

So you are not anything wrong. It won't be there until it is committed.

If you want to process the file in someway before saving it. You'll want to access it when its a temporary file. This answer can help you with this alternate way of accessing the file to get mimetype etc.

like image 198
Daniel Rucci Avatar answered Nov 14 '22 21:11

Daniel Rucci