передача обратного вызова как upload_to в FileField - PullRequest
4 голосов
/ 08 сентября 2010

У меня есть класс абстрактной модели UploadItem для обработки загруженных файлов. Я хочу, чтобы каждый подкласс мог определять путь upload_to. Для этого я передаю обратный вызов в конструктор FileField.

Это пример:

class UploadItem(models.Model):
    file = models.FileField(upload_to=UploadItem.get_directory) 


    class Meta:
        abstract = True
# I want videos to be storred in 'videos/' directory
class Video(UploadItem):
    def get_directory(self, instance, filename):
        return 'videos/'

Но это не работает, я получаю эту ошибку:

file = models.FileField(upload_to=UploadItem.get_directory) 
NameError: name 'UploadItem' is not defined

Ответы [ 2 ]

5 голосов
/ 08 сентября 2010

Ошибка естественна, учитывая, что на момент оценки

file = models.FileField(upload_to=UploadItem.get_directory) 

класс UploadItem еще не определен.Чтобы сделать это, вы можете сделать следующее:

def get_directory():
    pass

class UploadItem(models.Model):
    file = models.FileField(upload_to=get_directory)

    class Meta:
        abstract = True

Это не решит все ваши проблемы.Добавление (или переопределение) метода get_directory в классе Video не изменит свойства upload_to атрибута file модели.

Обновление

В документации говорится, что upload_to может быть вызвано.

Это также может вызываться, например, функция, которая будет вызываться для получения пути загрузки, включая имя файла.Этот вызываемый объект должен иметь возможность принимать два аргумента и возвращать путь в стиле Unix (с косой чертой) для передачи в систему хранения.

Учитывая это, мы можем написать пользовательскую функцию обратного вызова, например:

categories_and_paths = { 'video': 'videos/', 'photo': 'photos/' } # etc.
def get_directory(instance, filename):
    category = instance.category
    return categories_and_paths.get(category, '')

Instance здесь будет экземпляр соответствующей модели.Чтобы это работало, каждый экземпляр модели должен иметь поле категории.Мы можем добавить один в теле модели.

class Video(UploadItem):
    category = 'video'
1 голос
/ 26 декабря 2016

Это можно сделать аналогичным образом с некоторыми настройками, если вам нужно использовать свойства базового класса как часть подкласса:

import os

# ...

def get_directory(instance, filename):
    return instance.get_file_directory(filename)


class UploadItem(models.Model):
    FILE_DIRECTORY = 'files/'

    file = models.FileField(upload_to=get_directory) 

    class Meta:
        abstract = True

    @staticmethod
    def get_file_directory(filename):
        return os.path.join(UploadItem.FILE_DIRECTORY, filename)

А затем в подклассе:

class Video(UploadItem):
    FILE_DIRECTORY = 'videos/'

    def get_file_directory(self, filename):
        filename = os.path.join(self.FILE_DIRECTORY, filename)
        return super().get_document_path(filename)

Таким образом, у вас будет upload_to, что равно:

  • files для UploadItem
  • files/videos для Video

Может быть полезно для более сложных объектов, для которых требуется общее базовое свойство.

...