2017-07-27 3 views
0

Je suis en train de créer un dossier pour chaque utilisateur de mettre leur projet. Ainsi, leur dossier aura le chemin ..\project\id\filename, id est l'utilisateur id et filename est le nom du fichier. Maintenant, en utilisant les arguments autorisés pour upload_to (instance et filename) dans le Filefield, je me rends compte que instance.id seront None et le chemin vers le fichier sera ..\project\None\filename au lieu de ..\project\id\filename.Comment enregistrer un fichier sur un modèle en utilisant upload_to pour les chemins dynamiques

en train de lire la Django documentation upload_to j'ai vu ceci:

Dans la plupart des cas, cet objet aura pas été enregistré dans la base de données encore, donc si elle utilise la valeur par défaut AutoField, il pourrait ne pas encore avoir un valeur pour son champ clé primaire.

Mon interprétation est que la création d'un nouvel enregistrement et user_directory_path ne sont pas instancié en même temps, c'est, quand je l'appelle create le modèle Project, instance.id seront None. Ma question est la suivante: y a-t-il un moyen de contourner cela? Bien que je vois upload_to pratique, il n'est pas nécessairement pratique pour le chemin dynamique comme celui que je fais. Je pensais à créer l'enregistrement, puis ajouter le chemin du fichier dans une mise à jour, mais je suis à la recherche d'un moyen qui peut tout enregistrer en une seule étape.

models.py 
def user_directory_path(instance, filename): 
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename> 
    return 'project/{0}/{1}'.format(instance.user.id, filename) 


class Project(models.Model): 

    email = models.ForeignKey(User, 
          to_field="email", 
          max_length=50 
    ) 
    title = models.CharField(max_length=100) 
    date_created = models.DateTimeField(auto_now_add=True) 
    updated = models.DateTimeField(auto_now=True) 
    file = models.FileField(upload_to=user_directory_path, validators=[validate_file_type], null=True) 

Ceci est views.py lorsque le formulaire est validé. L'avis user_directory_path est appelé juste avant le create.

email = request.user.email 
title = request.POST.get('title', '') 
file = request.FILES['file'] 
filename = file.name 
instance = Usermie.objects.get(email=request.user.email) 

# Save to model 
user_directory_path(instance=instance, filename=filename) 
Project.objects.create(
    title=title, file=file, 
) 

Répondre

2

Si, comme vous le dites, l'identifiant que vous souhaitez utiliser dans le chemin du fichier est l'id l'utilisateur, pas l'identifiant du projet .. alors il n'y a pas de problème car l'utilisateur existe déjà lorsque vous sauvegardez le projet. Depuis email est une clé étrangère à User, vous simplement faire:

def user_directory_path(instance, filename): 
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename> 
    return 'project/{0}/{1}'.format(instance.email.id, filename) 

Mais je ferai remarquer que, dans la façon dont Django de faire les choses, ce qui rend un champ appelé email qui est une clé étrangère à User est en fait assez confus. Le champ dans la base de données sera appelé email_id .. et la valeur du champ de modèle renverra une instance de User .. pas l'adresse e-mail réelle, même si l'adresse e-mail est ce qui est stocké dans la colonne. Pour obtenir l'adresse e-mail que vous auriez besoin de faire l'une:

myproject.email.email  
myproject.email_id 

Ni l'un est très clair. Donc, sauf si vous avez une très bonne raison de le faire comme ça, vous devriez appeler le champ user et éliminer le to_field='email'. Autoriser Django à rejoindre les tables via id, qui est le comportement par défaut.

Ensuite, si vous avez besoin de l'adresse e-mail de l'utilisateur, vous pouvez l'obtenir à tout moment via

myproject.user.email 

Et le bonus est que si l'utilisateur change son adresse e-mail, il changera partout, vous ne devez pas compter sur les mises à jour en cascade pour corriger toutes les clés étrangères.

Croyez-moi, lorsque vous utilisez Django que vous voulez faire ForeignKey par id (par défaut), à moins qu'il ya une raison ...

+0

'Et le bonus est que si l'utilisateur change son adresse e-mail, il changera partout, vous ne devez pas compter sur les mises à jour en cascade pour réparer toutes les clés étrangères.» Très bonne raison d'utiliser 'id' dans ForeignKey. –

+0

Merci! Cela m'a en fait donné un meilleur aperçu. Je vais revenir en arrière et modifier mes champs de courrier électronique dans tous les autres modèles à l'ID utilisateur. Je n'ai pas réalisé le problème des utilisateurs changeant des email jusqu'à maintenant. Merci @MHassan. J'ai toujours su à ce sujet mais n'ai pas réalisé que je le faisais. – Jam1

+0

Je vous remercie de votre aide. L'autre chose que je devrais mentionner est qu'une fois que vous avez créé le projet vous ne pouvez pas permettre à l'utilisateur de changer parce que cela changerait le chemin du répertoire et les fichiers ne seraient plus trouvés par django. –

1

Une solution simple peut être objet sauver sans fichier et fichier de sauvegarde comme celui-ci

email = request.user.email 
title = request.POST.get('title', '') 
file = request.FILES['file'] 
filename = file.name 
instance = Usermie.objects.get(email=request.user.email) 

# Save to model 
user_directory_path(instance=instance, filename=filename) 
project = Project.objects.create(title=title) 
project.file = file 
project.save() 
+0

C'est une belle façon de regarder, mais faire comme cela, je ne l'obtenir clé primaire de l'utilisateur, mais la clé primaire du modèle de projet sera utilisée à la place. – Jam1

+1

Dans ce cas, vous devriez écrire '' project/{0}/{1} '. Format (instance.email_id, filename) 'comme le champ' email' est la clé étrangère du modèle 'User'. –

+0

Oh, je vois ce que vous dites, mais parce que mon champ email a l'attribut 'to_field =" email "' il retournera un chemin 'project/user_email/filename'. Je pourrais devoir changer quelque chose autour pour que cela fonctionne. Merci! – Jam1