upload_to must be a relative path

I have a company model and one of the fields is a company logo. I would like the file uploaded to the media root folder and then in a sub folder of the company. I would like to keep all company files in one place so every company has their own folder with media files in that folder.
So my field for company logo is specified like this

    def __str__():
        return 'company_name'

    logo_path = settings.MEDIA_ROOT + __str__()
    company_logo = models.ImageField(upload_to=logo_path)

and my MEDIA_ROOT setting is specified like this
MEDIA_ROOT = '/media/'

If I try to do makemigrations I get the following error:

Company.company_logo: (fields.E202) ImageField's 'upload_to' argument must be a relative path, not an absolute path.
        HINT: Remove the leading slash. 

So what I would like to know is if I remove the leading slash from the MEDIA_ROOT where in the system is the file for the company logo saved then?
How can I save the files in the format media/company_name/images for images and then the other files types as media/company_name/(file-type) for the other files types a person could upload to a company page?

The path you specify for upload_to is relative in that it has the MEDIA_ROOT/MEDIA_URL as the starting point. So if you use

company_logo = models.ImageField(upload_to='company_logos')

then the images would be saved to media/root/path/company_logos/, and would be accessible through yoursite.com/media/url/company_logos.

You want to use dynamic paths, based on an attribute of the company. You then have to set upload_to equal to a function that is evaluated each time an image is uploaded. You can read about this in the Django docs. Basically you want a function that does something like (outside of your model)

def get_img_upload_path(instance, filename):
    return f'{instance.name}/images/{filename}'

and then inside of your model

company_logo = models.ImageField(upload_to=get_img_upload_path)

You also will want to change your definition of the str method, as it’s invalid at the moment as an instance-bound method, and always outputs the same ‘company_name’ string. You might instead want:

def __str__(self):
    return self.name

(or self.company_name if you’ve named the attribute company_name, but simply name seems cleaner IMO, since the class itself indicates that it’s a company name)

1 Like

Thank you so much for the clear explanation I will follow your advice!