14

I want the filenames to be random and therefore I use upload_to function which returns a random filename like so:

from uuid import uuid4
import os
def get_random_filename(instance, filename):
    ext = filename.split('.')[-1]
    filename = "%s.%s" % (str(uuid4()), ext)
    return os.path.join('some/path/', filename)

# inside the model
class FooModel(models.Model):
    file = models.FileField(upload_to=get_random_filename)

However I would like to save the original filename to an attribute inside the model. Something like this does not work:

def get_random_filename(instance, filename):
    instance.filename = filename
    ext = filename.split('.')[-1]
    filename = "%s.%s" % (str(uuid4()), ext)
    return os.path.join('some/path/', filename)

# inside the model
class FooModel(models.Model):
    file = models.FileField(upload_to=get_random_filename)
    filename = models.CharField(max_length=128)

How can I do it?

Thank you.

miki725
  • 27,207
  • 17
  • 105
  • 121
  • It works perfectly fine in my tests. How exactly it doesn't work for you? What happens? Maybe the problem is in the view code? – rantanplan Jun 05 '12 at 23:53
  • once you do this and query the object from db, do you still see the value for filename? – miki725 Jun 06 '12 at 00:02
  • Yes. I actually tested it inside the shell. I killed it, launched it again and the filename was there. Actually at first I thought that you just forgot to do an `instance.save()` inside get_random_filename. But it seems that it isn't needed. – rantanplan Jun 06 '12 at 00:04
  • 1
    Which version of Django are you. I am 1.4 and does not work for me. – miki725 Jun 06 '12 at 00:23
  • Me too. Can't you post the relevant view code? Maybe there is some fundamental difference between your tests and mine. – rantanplan Jun 06 '12 at 00:27
  • im not using a view. just playing around in a shell – miki725 Jun 06 '12 at 00:31
  • OK, FYI I had as a reference(for testing in a shell) this section https://docs.djangoproject.com/en/1.4/ref/forms/api/#binding-uploaded-files-to-a-form. Maybe it's something obvious, maybe I'm just tired. Who knows. – rantanplan Jun 06 '12 at 00:34
  • @rantanplan Refer to the answer by okm. You were thinking all right, I just types it incorrectly. Thanx for looking and testing though. – miki725 Jun 06 '12 at 16:00

3 Answers3

8

The posted code normally works, perhaps the actual code is

class FooModel(models.Model):
    filename = models.CharField(max_length=128)
    file = models.FileField(upload_to=get_random_filename)

Note the switching of the ordering of the fields above.

This won't work because: the upload_to() is invoked by the pre_save(), here in the code, when the actual value of the FileField is required. You could find that the assignment to the attribute filename in the upload() is after the generating of the first param filename in the inserting sql. Thus, the assignment does not take effect in the generated SQL and only affects the instance itself.

If that's not the issue, please post the code you typed in shell.

okm
  • 23,575
  • 5
  • 83
  • 90
  • Thats was right on the spot. I retyped the core code in the question without realizing that I reversed the order. Thanx A LOT!!!!! – miki725 Jun 06 '12 at 15:53
  • And thanx for pointing out where that was happening. I was digging throught fields and models code but did not think of looking in sql. – miki725 Jun 06 '12 at 15:58
  • It seems to be broken with the Django 1.5... I was forced to add `super(FooModel, self).save(update_fields=['filename'])` in my model save overrided method. Any thoughts ? – dulaccc Dec 01 '13 at 12:48
1

You could go the route of populating the filename during the save process. Obviously you'll have to store the original file name in memory when your get_random_filename runs.

# inside the model
class FooModel(models.Model):
    file = models.FileField(upload_to=get_random_filename)
    filename = models.CharField(max_length=128)

    def save(self, force_insert=False, force_update=False):
        super(FooModel, self).save(force_insert, force_update)
            #Do your code here...
magicTuscan
  • 109
  • 5
  • What I don't like about this is that then save will have to happen twise. I would prefer something neater. – miki725 Jun 05 '12 at 23:44
-1

Just re-order your commands. https://docs.djangoproject.com/en/dev/topics/db/models/

def save(self, *args, **kwargs):
        do_something()
        super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
        do_something_else()
magicTuscan
  • 109
  • 5