Home > Software engineering >  Django - uploading file to folder named with id of the object
Django - uploading file to folder named with id of the object

Time:08-24

I was trying to make it this way but instead of {{product.id}}, folder is named None.

I read some articles about that, and I found out that is because folder is being made before whole object. So how should I do that to make it work?

models.py:

def getImageURL(self, filename):
    return "products_images/"   str(self.pk)   "/product_image.png"
    

class Product(models.Model):
    name = models.CharField(max_length=200)
    image = models.ImageField(upload_to=getImageURL, default="media/static/products_images/default.png" )

changed my funtion to:

def getImageURL(instance, filename):
    return "products_images/{}/product_image.png".format(instance.id)

But it works only while edditing existing object. When I'm trying to make a new one id is set to None.

Edit:

ok, finally I did it this way:

def getFileNumber():
    queryset = Product.objects.all().order_by('pk')
    last = queryset.last()
    last_id = last.id
    file_number = last_id 1
    return str(file_number)
    
def getImageURL(instance, filename):
    return "products_images/{}/product_image.png".format(getFileNumber())

class Product(models.Model):
    name = models.CharField(max_length=200)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    availability = models.IntegerField()
    price = models.DecimalField(max_digits=5, decimal_places=2)

Probably it is not the best way to do that but it's very simple to understand so why not to use it.

Some debugging:


def getFileNumber():
    queryset = Product.objects.all().order_by('pk')
    if queryset:
        last = queryset.last()
        last_id = last.id
        file_number = last_id 1
        return str(file_number)
    else:
        file_number=1
        return str(file_number)
    
def getImageURL(instance, filename):
    path = os.path.join("products_images/{}/product_image.png".format(getFileNumber()))
    print(path)
    if os.path.isfile("media/"   path):
        print("image with this id already exist, ")
        os.remove("media/"   path)
        return path
    else:
        return path

class Product(models.Model):
    name = models.CharField(max_length=200)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    availability = models.IntegerField()
    price = models.DecimalField(max_digits=5, decimal_places=2)

CodePudding user response:

The following is the issue:

  • The instance doesn't have a primary key yet before it is created
  • When the instance gets saved in the database, then you can get the primary key

Maybe use signals, somehow? Do the logic it in the view after saving it?

Alternative option to pk:

You can generate a UUID for the object.

    id = models.UUIDField(
         primary_key = True,
         default = uuid.uuid4,
         editable = False)

Or alternatively:

Have a look at this package, you can set the uuid alphabet, length, and some more. This would allow you access to the id, for sure (same with uuid).

https://pypi.org/project/shortuuid/

id = ShortUUIDField(
        length=16,
        max_length=40,
        prefix="id_",
        alphabet="abcdefg1234",
        primary_key=True,
    )

Sidenote:

def getImageURL(self, filename): #Self would normally be defined as instance, but I suppose this is just semantics.

CodePudding user response:

This is because the id is saved to the model after calling save(). The first guess would be to use save() to get the object in return. But save() does not return anything. So I put together a little example

class Thingy(models.Model):
    name = models.CharField(
        _('name'),
        max_length=64,
    )

    def save(self, *args, **kwargs):
        super(Thingy, self).save(*args, **kwargs)
        print(self.pk)

My test was:

>>> t = Thingy(name="lol")
>>> t.save()
1

And it printed the primary key since save() mutades self. You could check if the pk exists before save(). If yes, just add the image path. If not. Execute save() first, manipulate the image path using pk, and save the object again. It is not elegant in any way, but I guess that is the only way.

  • Related