Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading file data during form's clean method

So, I'm working on implementing the answer to my previous question.

Here's my model:

class Talk(models.Model):
  title        = models.CharField(max_length=200)
  mp3          = models.FileField(upload_to = u'talks/', max_length=200)

Here's my form:

class TalkForm(forms.ModelForm):
  def clean(self):
    super(TalkForm, self).clean()
    cleaned_data = self.cleaned_data

    if u'mp3' in self.files:
      from mutagen.mp3 import MP3
      if hasattr(self.files['mp3'], 'temporary_file_path'):
        audio = MP3(self.files['mp3'].temporary_file_path())
      else:
        # What goes here?
        audio = None # setting to None for now
      ...
    return cleaned_data

  class Meta:
    model = Talk

Mutagen needs file-like objects or filenames on disk (I think) - the first case (where the uploaded file is larger than the size of file handled in memory) works fine, but I don't know how to handle InMemoryUploadedFile that I get otherwise. I've tried:

# TypeError (coercing to Unicode: need string or buffer, InMemoryUploadedFile found)
audio = MP3(self.files['mp3'])

# TypeError (coercing to Unicode: need string or buffer, cStringIO.StringO found)
audio = MP3(self.files['mp3'].file)

# Hangs seemingly indefinitely on my test file (~800KB)
audio = MP3(self.files['mp3'].file.read())

Is there something wrong with mutagen, or am I doing it wrong?

After rebus' answer

Modifying the FILE_UPLOAD_HANDLERS setting on the fly in my ModelAdmin class like this:

def add_view(self, request, form_url='', extra_context=None):
  request.upload_handlers = [TemporaryFileUploadHandler()]
  return super(TalkAdmin, self).add_view(request, form_url, extra_context)

Gets me the following error 500 when I hit submit:

You cannot set the upload handlers after the upload has been processed.

even though I'm doing it as early as I possibly can!

Also, I'm not sure I've got a save method on the object I'm getting back (I've looked in dir(self.files['mp3'].file) and dir(self.files['mp3'])).

like image 752
Dominic Rodger Avatar asked May 10 '10 21:05

Dominic Rodger


People also ask

What does cleaned data do in Django?

cleaned_data is where all validated fields are stored.

What is clean method in Django?

The clean() method on a Field subclass is responsible for running to_python() , validate() , and run_validators() in the correct order and propagating their errors. If, at any time, any of the methods raise ValidationError , the validation stops and that error is raised.

What method can you use to check if form data has changed when using a form instance?

Use the has_changed() method on your Form when you need to check if the form data has been changed from the initial data. has_changed() will be True if the data from request.

How do you remove this field is required Django?

If yes try to disable this behavior, set the novalidate attribute on the form tag As <form action="{% url 'new_page' %}", method="POST" novalidate> in your html file.


1 Answers

You could try to change your FILE_UPLOAD_HANDLERS in such a way so Django always uses temporay file handler:

FILE_UPLOAD_HANDLERS default:

("django.core.files.uploadhandler.MemoryFileUploadHandler",
 "django.core.files.uploadhandler.TemporaryFileUploadHandler",)

So you could leave only TemporaryFileUploadHandler by overriding the setting in your settings.py.

Edit:

Much simpler, should have thought of it at the first place :(:

from your.models import Talk
mp3 = self.files['mp3']
f = Talk.mp3.save('somename.mp3', mp3)
MP3(f.mp3.path)
>>> {'TRCK': TRCK(encoding=0, text=[u'5'])}

You can save InMemoryUploadedFile to the disk this way and then use the path to that file to work with mutagen.

Edit:

Same thing without a models instance.

import os

from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from django.conf import settings

from mutagen.mp3 import MP3

mp3 = request.FILES['mp3'] # or self.files['mp3'] in your form

path = default_storage.save('tmp/somename.mp3', ContentFile(mp3.read()))
MP3(os.path.join(settings.MEDIA_ROOT, path))

Note that it's saving the file in MEDIA_ROOT, when i try to save it anywhere else i get SuspiciousOperation since there are limits to where you can write... You should delete this file after examining it i guess, the real thing will be on your model...

path = default_storage.delete('tmp/somename.mp3')
like image 82
Davor Lucic Avatar answered Sep 30 '22 23:09

Davor Lucic