Django - using instance.id while uploading image

Viewed 2482

I was referring to this youtube video, to understand how to upload image using ImageField. He has explained how to use the instance.id while saving the image. I tried it, but instance.id is returning None. Whereas for him it worked perfectly. The following is the code:

#models.py
import os

def get_image_path(instance, filename):
    return os.path.join(str(instance.id), filename)

class AdProfile(models.Model):
    name = models.CharField(max_length=100)
    profile_image = models.ImageField(upload_to=get_image_path, blank=True, null=True)

Whenever the file is saved, its saving as None/filename.

Even this link informs the same. I am using Django 10.5 and MySQL database.

What might be the problem?

2 Answers

Using Raja Simon's answer, there is recipe to process all FileField in the model

class MyModel(models.Model):

    file_field = models.FileField(upload_to=upload_to, blank=True, null=True)

    def save(self, *args, **kwargs):
        if self.id is None:
            saved = []
            for f in self.__class__._meta.get_fields():
                if isinstance(f, models.FileField):
                    saved.append((f.name, getattr(self, f.name)))
                    setattr(self, f.name, None)

            super(self.__class__, self).save(*args, **kwargs)

            for name, val in saved:
                setattr(self, name, val)
        super(self.__class__, self).save(*args, **kwargs)

Moreover, we can make file location dynamic, i.e. based not only on self.id, but also on id of foreign key or whatever. Just iterate over fields and check if path changed.

def upload_to(o, fn):
    if o.parent and o.parent.id:
        return parent_upload_to(o.parent, fn)

    return "my_temp_dir/{}/{}".format(o.id, fn)


class MyModel(models.Model):

    parent = models.ForeignKey(Parent)

    def save(self, *args, **kwargs):

        # .... code from save() above here

        for f in [f for f in self.__class__._meta.get_fields() if isinstance(f, models.FileField)]:

            upload_to = f.upload_to

            f = getattr(self, f.name)  # f is FileField now

            if f and callable(upload_to):
                _, fn = os.path.split(f.name)
                old_name = os.path.normpath(f.name)
                new_name = os.path.normpath(upload_to(self, fn))

                if old_name != new_name:

                    old_path = os.path.join(settings.MEDIA_ROOT, old_name)
                    new_path = os.path.join(settings.MEDIA_ROOT, new_name)

                    new_dir, _ = os.path.split(new_path)
                    if not os.path.exists(new_dir):
                        print "Making  dir {}", new_dir
                        os.makedirs(new_dir)

                    print "Moving {} to {}".format(old_path, new_path)
                    try:
                        os.rename(old_path, new_path)
                        f.name = new_name

                    except WindowsError as e:
                        print "Can not move file, WindowsError: {}".format(e)

        super(self.__class__, self).save(*args, **kwargs)
Related