Django FileField с upload_to определяется во время выполнения

Я пытаюсь настроить свои загрузки таким образом, что если пользователь joe загружает файл, он переходит в MEDIA_ROOT/joe, а не все файлы идут в MEDIA_ROOT. Проблема в том, что я не знаю, как определить это в модели. Вот как это выглядит в настоящее время:

class Content(models.Model):
    name = models.CharField(max_length=200)
    user = models.ForeignKey(User)
    file = models.FileField(upload_to='.')

Так что я хочу вместо '.' как upload_to, укажите имя пользователя.

Я понимаю, что с Django 1.0 вы можете определить свою собственную функцию для обработки upload_to, но эта функция не имеет представления о том, кто из пользователей будет либо, так что я немного потерян.

Спасибо за помощь!

Ответ 1

Вы, вероятно, читаете документацию, поэтому здесь можно сделать простой пример:

def content_file_name(instance, filename):
    return '/'.join(['content', instance.user.username, filename])

class Content(models.Model):
    name = models.CharField(max_length=200)
    user = models.ForeignKey(User)
    file = models.FileField(upload_to=content_file_name)

Как вы можете видеть, вам даже не нужно использовать указанное имя файла - вы можете переопределить это в своем upload_to вызываемом тоже, если хотите.

Ответ 2

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

file = models.FileField(
    upload_to=lambda instance, filename: '/'.join(['mymodel', str(instance.pk), filename]),
)

Ответ 3

Заметка об использовании значения pk объекта 'instance'. Согласно документации:

В большинстве случаев этот объект еще не был сохранен в базе данных, поэтому, если он использует AutoField по умолчанию, он может еще не иметь значения для своего поля первичного ключа.

Следовательно, справедливость использования pk зависит от того, как определена ваша конкретная модель.

Ответ 4

Если у вас есть проблемы с миграциями, вы, вероятно, должны использовать @deconstructible decorator.

import datetime
import os
import unicodedata

from django.core.files.storage import default_storage
from django.utils.deconstruct import deconstructible
from django.utils.encoding import force_text, force_str


@deconstructible
class UploadToPath(object):
    def __init__(self, upload_to):
        self.upload_to = upload_to

    def __call__(self, instance, filename):
        return self.generate_filename(filename)

    def get_directory_name(self):
        return os.path.normpath(force_text(datetime.datetime.now().strftime(force_str(self.upload_to))))

    def get_filename(self, filename):
        filename = default_storage.get_valid_name(os.path.basename(filename))
        filename = force_text(filename)
        filename = unicodedata.normalize('NFKD', filename).encode('ascii', 'ignore').decode('ascii')
        return os.path.normpath(filename)

    def generate_filename(self, filename):
        return os.path.join(self.get_directory_name(), self.get_filename(filename))

Использование:

class MyModel(models.Model):
    file = models.FileField(upload_to=UploadToPath('files/%Y/%m/%d'), max_length=255)