Django admin file upload with current model id

The image file gets saved before Gallery instance. So you have to split the saving to two phases by using signals w/ Gallery instance itself carrying the state:

from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver

_UNSAVED_FILEFIELD = 'unsaved_filefield'

@receiver(pre_save, sender=Image)
def skip_saving_file(sender, instance, **kwargs):
    if not instance.pk and not hasattr(instance, _UNSAVED_FILEFIELD):
        setattr(instance, _UNSAVED_FILEFIELD, instance.image)
        instance.image = None

@receiver(post_save, sender=Image)
def save_file(sender, instance, created, **kwargs):
    if created and hasattr(instance, _UNSAVED_FILEFIELD):
        instance.image = getattr(instance, _UNSAVED_FILEFIELD)
        instance.save()        
        # delete it if you feel uncomfortable...
        # instance.__dict__.pop(_UNSAVED_FILEFIELD)

The upload_path_handler looks like

def upload_path_handler(instance, filename):
    import os.path
    fn, ext = os.path.splitext(filename)
    return "site_media/images/gallery/{id}{ext}".format(id=instance.pk, ext=ext)

I suggest using ImageField instead of FileField for type-checking if the field is for image uploading only. Also, you may want to normalize filename extension (which is unnecessary because of the mimetype) like

def normalize_ext(image_field):
    try:
        from PIL import Image
    except ImportError:
        import Image
    ext = Image.open(image_field).format
    if hasattr(image_field, 'seek') and callable(image_field.seek):
       image_field.seek(0)
    ext = ext.lower()
    if ext == 'jpeg':
        ext = 'jpg'
    return '.' + ext

For Django 2.2, follow the below code.

def save(self, *args, **kwargs):
    if self.pk is None:
        saved_image = self.image
        self.image = None
        super(Gallery, self).save(*args, **kwargs)
        self.image = saved_image
        if 'force_insert' in kwargs:
            kwargs.pop('force_insert')

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

Add the above code snippet to your 'class Gallery'.

P.S.: This will work for DRF as well when you save via views.py. Note that second if (condition) is required for DRF.


I ran into the same problem. Okm's answer sent me on the right path but it seems to me it is possible to get the same functionality by just overriding the save() method of your Model.

def save(self, *args, **kwargs):
    if self.pk is None:
        saved_image = self.image
        self.image = None
        super(Material, self).save(*args, **kwargs)
        self.image = saved_image

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

This definitely saves the information correctly.