django instance.id=None when uploading image

Question:

instance.id is returning None when upload images through the admin page. The idea was to upload all the images of each Residence to a different folder. Here’s my code:

models.py

from django.db import models
import os

def get_image_path(instance, filename):
    return os.path.join('photos', "residence_%s" % instance.id, filename)


# Create your models here.
class Residence(models.Model):
    big_image = models.ImageField("Main Image",upload_to=get_image_path)
    small_images = models.ImageField("Small Images",upload_to=get_image_path, blank=True, null=True)

settings.py

MEDIA_URL = '/media/'

EDIT: It works if I modify the image after the model is already added.

Asked By: user7351292

||

Answers:

You can’t do it in that way unless you implement your custom dynamic file upload field. Because you try to access instance.id, but instance isn’t saved yet and doesn’t have an id.

Here you are some resources that will help you achieve what you want:

Answered By: wencakisa

Another nice way to solve this problem that requires much less code is to have your model use a UUID for the primary key rather then the database generated id. This means at the point the model is saved for the first time the UUID is already known and can be used with any upload_to callbacks.

So for the original example you would do something like this

from django.db import models

import uuid
import os

def get_image_path(instance, filename):
    return os.path.join('photos', "residence_%s" % str(instance.id), filename)

# Create your models here.
class Residence(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    big_image = models.ImageField("Main Image",upload_to=get_image_path)
    small_images = models.ImageField("Small Images",upload_to=get_image_path, blank=True, null=True)

See Django’s UUIDField reference for more info

Answered By: TimmyGee

If you don’t specify an update_to on the ImageField you could upload to your media root then change the path using a post_save signal.

@receiver(post_save, sender=Product)
def update_file_path(instance, created, **kwargs):
    if created:
        initial_path = instance.image.path
        new_path = settings.MEDIA_ROOT + f'/product_{instance.id}/{instance.image.name}'
        os.makedirs(os.path.dirname(new_path), exist_ok=True)
        os.rename(initial_path, new_path)
        instance.image = new_path
        instance.save()
Answered By: bdoubleu

you can create a model instance by passing cleaned data from your form as **kwargs to django model i did it that way & its much easier than anything else

in your views post method add this (this code is from my project not adapted to this question)

    pk = request.session['_auth_user_id']
    user_obj = User.objects.get(pk=pk)


    lab_form_instance = lab_form(request.POST,request.FILES)
    lab_form_instance.save(commit=False)
    # here you can put the form.is_valid() statement
    lab_form_instance.cleaned_data['owner'] =user_obj # here iam adding additional needed data for the model
    obj = lab(**lab_form_instance.cleaned_data)
    obj.save()
Answered By: Peter Nasser Mosaad
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.