Обработка загрузки файлов перед сохранением объекта

У меня есть такая модель:

class Talk(BaseModel):
  title        = models.CharField(max_length=200)
  mp3          = models.FileField(upload_to = u'talks/', max_length=200)
  seconds      = models.IntegerField(blank = True, null = True)

Я хочу проверить перед сохранением, что загруженный файл является MP3, например:

def is_mp3(path_to_file):
  from mutagen.mp3 import MP3
  audio = MP3(path_to_file)
  return not audio.info.sketchy

Как только я буду уверен, что у меня есть MP3, я хочу сохранить продолжительность разговора в атрибуте секунд, например:

audio = MP3(path_to_file)
self.seconds = audio.info.length

Проблема в том, что перед сохранением загруженный файл не имеет пути (см. этот билет, закрыт как wontfix), поэтому я не могу обработать MP3.

Я хотел бы вызвать хорошую ошибку проверки, чтобы ModelForms мог отображать полезную ошибку («Ты идиот, ты не загрузил MP3» или что-то в этом роде).

Любая идея, как я могу получить доступ к файлу до его сохранения?

пс. Если кто-нибудь знает лучший способ проверки файлов в формате MP3, я весь в слух - я также хочу иметь возможность возиться с данными ID3 (установить исполнителя, альбом, название и, возможно, обложку альбома, поэтому мне нужно, чтобы он обрабатывался мутаген).


person Dominic Rodger    schedule 09.05.2010    source источник


Ответы (3)


Вы можете получить доступ к данным файла в request.FILES находясь в вашем поле зрения.

Я думаю, что лучший способ — привязать загруженные файлы к form, переопределите формы чистый метод, получите объект UploadedFile из очищенных_данных, подтвердите его, как хотите, затем переопределите save method и заполните экземпляр модели информацией о файле, а затем сохраните его.

person Davor Lucic    schedule 09.05.2010
comment
+1 и принято! Кажется, это правильный путь - кажется позорным, что нет элегантного способа сделать это с использованием новомодной проверки модели. - person Dominic Rodger; 10.05.2010
comment
Может быть, вы могли бы, но это довольно новое, и я еще недостаточно знаком с ним. - person Davor Lucic; 10.05.2010
comment
Не могли бы вы написать пример? - person Zack Plauché; 01.07.2021

более чистый способ получить файл перед сохранением выглядит следующим образом:

from django.core.exceptions import ValidationError

#this go in your class Model
def clean(self):
    try:
        f = self.mp3.file #the file in Memory
    except ValueError:
        raise ValidationError("A File is needed")
    f.__class__ #this prints <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
    processfile(f)

и если нам нужен путь, то ответ в этом другом вопросе

person sacabuche    schedule 30.03.2011

Вы можете следовать методике, используемой ImageField. где он проверяет заголовок файла, а затем возвращается к началу файла.

class ImageField(FileField):
    # ...    
    def to_python(self, data):
        f = super(ImageField, self).to_python(data)
        # ...
        # We need to get a file object for Pillow. We might have a path or we might
        # have to read the data into memory.
        if hasattr(data, 'temporary_file_path'):
            file = data.temporary_file_path()
        else:
            if hasattr(data, 'read'):
                file = BytesIO(data.read())
            else:
                file = BytesIO(data['content'])

        try:
            # ...
        except Exception:
            # Pillow doesn't recognize it as an image.
            six.reraise(ValidationError, ValidationError(
                self.error_messages['invalid_image'],
                code='invalid_image',
            ), sys.exc_info()[2])
        if hasattr(f, 'seek') and callable(f.seek):
            f.seek(0)
        return f
person Don Kirkby    schedule 02.08.2018