Django: Any way to change "upload_to" property of FileField without resorting to magic?

django, python

Solution

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.

Problem

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?

Original source