Django ImageField change file name on upload

You can replace the string your assigning to upload_to with a callable as described in the docs. However, I suspect the primary key may not be available at the point the upload_to parameter is used.


Django 1.7 and newer won't make migration with function like this. Based on answer by @miki725 and this ticket, you need to make your function like this:

import os
from uuid import uuid4
from django.utils.deconstruct import deconstructible

@deconstructible
class UploadToPathAndRename(object):

    def __init__(self, path):
        self.sub_path = path

    def __call__(self, instance, filename):
        ext = filename.split('.')[-1]
        # get filename
        if instance.pk:
            filename = '{}.{}'.format(instance.pk, ext)
        else:
            # set filename as random string
            filename = '{}.{}'.format(uuid4().hex, ext)
        # return the whole path to the file
        return os.path.join(self.sub_path, filename)

FileField(upload_to=UploadToPathAndRename(os.path.join(MEDIA_ROOT, 'upload', 'here'), ...)

You can pass a function into upload_to field:

def f(instance, filename):
    ext = filename.split('.')[-1]
    if instance.pk:
        return '{}.{}'.format(instance.pk, ext)
    else:
        pass
        # do something if pk is not there yet

My suggestions would be to return a random filename instead of {pk}.{ext}. As a bonus, it will be more secure.

What happens is that Django will call this function to determine where the file should be uploaded to. That means that your function is responsible for returning the whole path of the file including the filename. Below is modified function where you can specify where to upload to and how to use it:

import os
from uuid import uuid4

def path_and_rename(path):
    def wrapper(instance, filename):
        ext = filename.split('.')[-1]
        # get filename
        if instance.pk:
            filename = '{}.{}'.format(instance.pk, ext)
        else:
            # set filename as random string
            filename = '{}.{}'.format(uuid4().hex, ext)
        # return the whole path to the file
        return os.path.join(path, filename)
    return wrapper

FileField(upload_to=path_and_rename('upload/here/'), ...)

Tags:

Django