44

Is it possible to change the file name of an uploaded file in django? I searched, but couldn't find any answer.

My requirement is whenever a file is uploaded its file name should be changed in the following format.

format = userid + transaction_uuid + file_extension

Thank you very much...

jww
  • 97,681
  • 90
  • 411
  • 885
Software Enthusiastic
  • 25,147
  • 16
  • 58
  • 68

9 Answers9

63

How are you uploading the file? I assume with the FileField.

The documentation for FileField.upload_to says that the upload_to field,

may also be a callable, such as a function, which will be called to obtain the upload path, including the filename. This callable must be able to accept two arguments, and return a Unix-style path (with forward slashes) to be passed along to the storage system. The two arguments that will be passed are:

"instance": An instance of the model where the FileField is defined. More specifically, this is the particular instance where the current file is being attached.

"filename":The filename that was originally given to the file. This may or may not be taken into account when determining the final destination path.

So it looks like you just need to make a function to do your name handling and return the path.

def update_filename(instance, filename):
    path = "upload/path/"
    format = instance.userid + instance.transaction_uuid + instance.file_extension
    return os.path.join(path, format)
Raffi
  • 3,068
  • 31
  • 33
monkut
  • 42,176
  • 24
  • 124
  • 155
  • 2
    excepting if the file exists; does the storage butcher your creation to get a unique name? – John Mee May 31 '10 at 13:21
  • 4
    For others coming to the answer (like me) please note that the path cannot start with a / otherwise django will through an error due to a "Suspicious Operation" so you use @monkut 's example `path = "/upload/path/"` should be `path = "upload/path/"` – Seb Ashton Mar 03 '12 at 13:37
  • be careful that if you create your custom validation function in a form like `def clean_my_audio_file_field(self):` then **the result of this function will override** whatever you do in the `upload_to= parameter` – Olivier Pons Dec 08 '15 at 12:04
  • What if you want the file name to include the value from a drop down menu? How can you make that value available to `models.py`? – Renel Chesak Aug 21 '17 at 13:48
  • @SoftwareEnthusiastic Can you let me know this function def update_file_name() where this was written in views.py? – Cyley Simon Apr 24 '18 at 07:08
  • What if you need instance.id to generate the filename? – Jorge Arévalo Sep 11 '19 at 11:19
  • include it in the returned string, `return f'{instance.id}/{filename}'` – monkut Sep 12 '19 at 07:59
13

You need to have a FileField with the upload_to that calls to a callback, see [1]

Your callback should call a wrapper method which gets an instance as one of the params and filename as the other. [2]

Change it the way you like and return the new path [3]

1. LOGIC

FileField(..., upload_to=method_call(params),....)

2. define method

def method_call(params):
    return u'abc'

3. Wrapper:

def wrapper(instance, filename):
    return method

this is the rapper method that you need for getting the instance.

def wrapper(instance, filename):
... Your logic
...
return wrapper

Complete Code

def path_and_rename(path, prefix):
    def wrapper(instance, filename):
        ext = filename.split('.')[-1]
        project = "pid_%s" % (instance.project.id,)
        # get filename
        if instance.pk:
            complaint_id = "cid_%s" % (instance.pk,)
            filename = '{}.{}.{}.{}'.format(prefix, project, complaint_id, ext)
        else:
            # set filename as random string
            random_id = "rid_%s" % (uuid4().hex,)
            filename = '{}.{}.{}.{}'.format(prefix, project, random_id, ext)
            # return the whole path to the file
        return os.path.join(path, filename)

    return wrapper

Call to Method

sales_attach = models.FileField("Attachment", upload_to=path_and_rename("complaint_files", 'sales'), max_length=500,
                                help_text="Browse a file")

Hope this helps. Thanks.

A.J.
  • 8,557
  • 11
  • 61
  • 89
11

if you want your function re-usable:

import hashlib
import datetime
import os
from functools import partial

def _update_filename(instance, filename, path):
    path = path

    filename = "..."

    return os.path.join(path, filename)

def upload_to(path):
    return partial(_update_filename, path=path)

You just have to use it this way:

document = models.FileField(upload_to=upload_to("my/path"))
nicolas
  • 880
  • 2
  • 9
  • 19
2
import random
import os
def generate_unique_name(path):
    def wrapper(instance, filename):
        extension = "." + filename.split('.')[-1]
        filename = str(random.randint(10,99)) + str(random.randint(10,99)) + str(random.randint(10,99)) + str(random.randint(10,99))  + extension
        return os.path.join(path, filename)
    return wrapper

#You just have to use it this way:#

 photo = models.FileField("Attachment", upload_to=generate_unique_name("pics"),max_length=500,help_text="Browse a photo")
2

Incase this may help anyone.

import os
import uuid
import random

from datetime import datetime 

def user_directory_path(instance, filename):
    # Get Current Date
    todays_date = datetime.now()

    path = "uploads/{}/{}/{}/".format(todays_date.year, todays_date.month, todays_date.day)
    extension = "." + filename.split('.')[-1]
    stringId = str(uuid.uuid4())
    randInt = str(random.randint(10, 99))

    # Filename reformat
    filename_reformat = stringId + randInt + extension

    return os.path.join(path, filename_reformat)


class MyModel(models.Model):
    upload = models.FileField(upload_to=user_directory_path)
Haykins
  • 101
  • 1
  • 3
1

That much code is not needed you can just use single line code fille._name=userid + transaction_uuid + file_extension

Like

class xyz(models.Model):
   file = models.FileField(upload_to="notice/")

   def add(request):
      file = request.POST['file']
      file._name = request.user.id + transaction_uuid +"."+ file._name.split('.')[1]

you can overwrite file name by overwriting _name value of file object.

Aniket Pawar
  • 56
  • 1
  • 2
0

You can try this also work, name_of_submission is any column you want to attach the file

import os

def update_filename(instance, filename):
    path = "upload/path/"
    ext = filename.split('.')[-1]
    format = instance.name_of_submission +    str(instance.id_application) + "." + ext
    print(format)
    return os.path.join(path, format)
carmel26
  • 1
  • 2
-1

The basic way is

import os

os.rename('a.txt', 'b.html')

For your situation, it would probably look like

os.rename ("a.txt", "{id}{uuid}.{ext}".format(id=userid, uuid=transaction_uuid, ext=file_extension))
crystalattice
  • 5,031
  • 12
  • 40
  • 57
Bárbara Este
  • 103
  • 2
  • 11
-1

Hi, i check all the answers, but someone are not updated, this is how in 2022 works whith clean code and following the Django Documentation Here, remember that you need to make a MIGRATION to make this work:

 def AvatarSave(instance, filename):
    #this line changes the name of the file to the user name and put the file extension at the end after the point
    return 'users/avatars/{0}.{1}'.format(instance.id,filename.split('.')[-1])

 avatar = models.ImageField(_("avatar"),upload_to=AvatarSave)
  • This is just a worse version of the accepted answer by @monkut. You even link to the same old version `3.2` of the Django docs. In contrast to yours, the accepted answer actually relates to the specifics of the OP with how their file should be named. You used some avatar example with an `ImageField` that has nothing to do with the OP. – Daniil Fajnberg Aug 31 '22 at 10:45
  • sorry this work, for the latest version [link](https://docs.djangoproject.com/en/4.1/ref/models/fields/#django.db.models.FileField.upload_to), I do not remember why the other code not work for me – Oliver Sitán Sep 05 '22 at 02:37
  • At the end i use this code, because i need the images whit the exact same name every time and i use other methods to make a cache of the images on my app, I am open to see comments about that: ```python def AvatarSave(instance, filename): file = 'users/avatars/{0}.{1}'.format(instance.id, filename.split('.')[-1]) fullFile = os.path.join(settings.MEDIA_ROOT, file) if exists(fullFile): os.remove(os.path.join(settings.MEDIA_ROOT, fullFile)) return file ``` – Oliver Sitán Sep 05 '22 at 02:40