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

See this blog post ... It's pretty old, so maybe everything has changed. But in my experiments they did not. To dynamically change the path to the FileField upload_to file, you must use signals and create custom model fields . Nastya. I canโ€™t imagine that having a dynamic loading path is a special use case that is not covered by the standard Django platform? Am I missing something? Is there any other way to do 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 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 them all. For this, in particular, the file is uploaded to the default path 'media/' .

I tried to cross the model parameter for the parameters and pass these parameters to the dict object, create a MyModel object, set the MyModel.fileUpload.upload_to parameter, and then copy the dict to the model and save. Does not work.

I also tried to override the __init__ method, but guess what? It is so early in creating an object that it has not yet defined self.email ! So this will not work.

Any ideas, or should I follow a secret solution outlined in the original link?

+2
source share
3 answers

So there is actually a fairly simple solution, in FileField' field, the upload_to keyword argument can actually take a function as a parameter. The function that you specify in your keyword argument can actually take a function as a parameter. The function that you specify in your upload_to` kwarg must 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, the instance is an instance of your model that has a FileField, and filename is the file name of the downloaded file. Thus, your model, as in your example above, will look like this:

 def MyModel(models.Model): fileUpload = models.FileField(upload_to=my_awesome_upload_function, null=True, blank=True) 

If that makes sense, you can now modify my_awesome_upload_function to create a path to download the file based on your preferences, given the model instance and file name of the downloaded file.

+6
source

You can use this combination in the 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 line that you want to use to name the directory in which it will save the file. Thus, in this case, the path will be as follows: /media/content/<file_name>

+3
source

The same blog I referenced earlier also mentioned a 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 it uses the identifier of the actual object. Of course, you can use whatever you want. But that answers my question.

+1
source

Source: https://habr.com/ru/post/1207953/


All Articles