See this blog post... It's quite old so maybe things have changed. But in my experimenting they have not. In order to change the model field FileField upload_to
path dynamically, you must resort to using signals
and creating custom model fields
. Nasty. I can't imagine that having a dynamic upload path is such a special use case that it's not addressed by the standard Django framework? Am I missing something? Is there any other way to accomplish this?
In essence I want to do this:
def MyModel(models.Model):
fileUpload = models.FileField(upload_to='media/', null=True, blank=True)
def save(self, **kwargs):
# Retrieve the user's id/pk from their profile
up = UserProfile.objects.get(email=self.email)
# All their uploads go into their own directory
self.file_image.upload_to = up.id
super(MyModel, self).save()
However, in the 10 different implementations I tried, Django hates all of them. For this one in particular, the file is uploaded to the default path 'media/'
.
I've tried scraping a modelform for parameters and passing those parameters into a dict object, create a MyModel object, set the MyModel.fileUpload.upload_to parameter, then copy the dict into the model and save. Doesn't work.
I also tried to override the __init__
method, but guess what? That is so early in the object creation that it doesn't actually have self.email
defined yet! So that doesn't work.
Any ideas or must I follow the arcane solution outlined in the original link?
So there is actually a fairly easy solution to this, in the FileField
field, the upload_to
keyword argument can actually take a function as a parameter. The function that you specify in your upload_to
kwarg should have this signature.
def my_awesome_upload_function(instance, filename):
""" this function has to return the location to upload the file """
return os.path.join('/media/%s/' % instance.id, filename)
In this case, instance
is the instance of your model that has the FileField
, and filename
is the filename of the uploaded file. So your model like in your example above would look like this:
def MyModel(models.Model):
fileUpload = models.FileField(upload_to=my_awesome_upload_function, null=True, blank=True)
If this makes sense you can now change my_awesome_upload_function
to generate you path to upload the file to based on your preference, given the model instance and filename of the file that has been uploaded.
You can use this combination in your models.py
file for upload_to
:
def content_file_name(instance, filename):
return '/'.join(['content', filename])
class Book(models.Model):
title = models.CharField(max_length=80)
file = models.FileField(upload_to=content_file_name, null=False, verbose_name="File")
You can change the word 'content' to any string that you want to use to name the directory that it's going to save the file to. So in this case the path would be: /media/content/<file_name>
Same blog, that I referenced before, also mentioned the newer solution.
import os
from django.db import models
def get_image_path(instance, filename):
return os.path.join('photos', str(instance.id), filename)
class Photo(models.Model):
image = models.ImageField(upload_to=get_image_path)
Here he uses the id of the actual object. Of course you can use whatever you want. But this answers my question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With