web-dev-qa-db-fra.com

Django FileField avec upload_to déterminé à l'exécution

J'essaie de configurer mes téléchargements de sorte que si l'utilisateur joe télécharge un fichier, il passe à MEDIA_ROOT/joe, par opposition à ce que les fichiers de tout le monde aillent à MEDIA_ROOT. Le problème est que je ne sais pas comment définir cela dans le modèle. Voici à quoi cela ressemble actuellement:

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

Donc ce que je veux, c'est au lieu de "." comme upload_to, que ce soit le nom de l'utilisateur.

Je comprends qu'à partir de Django 1.0, vous pouvez définir votre propre fonction pour gérer le upload_to mais cette fonction n'a aucune idée de qui sera l'utilisateur, donc je suis un peu perdu.

Merci pour l'aide!

126
Teebes

Vous avez probablement lu la documentation , voici donc un exemple simple pour donner un sens:

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)

Comme vous pouvez le voir, vous n'avez même pas besoin d'utiliser le nom de fichier donné - vous pouvez également le remplacer dans votre appelable upload_to si vous le souhaitez.

248
SmileyChris

Cela a vraiment aidé. Par souci de concision, j'ai décidé d'utiliser lambda dans mon cas:

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

Remarque sur l'utilisation de la valeur pk de l'objet 'instance'. Selon la documentation:

Dans la plupart des cas, cet objet n'a pas encore été enregistré dans la base de données. Par conséquent, s'il utilise le champ automatique par défaut, il se peut qu'il n'ait pas encore de valeur pour son champ de clé primaire.

Par conséquent, la validité de l'utilisation de pk dépend de la façon dont votre modèle particulier est défini.

4
Max Dudzinski

Si vous rencontrez des problèmes avec les migrations, vous devriez probablement utiliser @deconstructible décorateur.

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))

Usage:

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