views:

97

answers:

2

I have a Django app with an image field (a custom ThumbnailImageField type) that auto-generates the file path for an image based on the title, type, and country of the item the image is attached to (upload_ to = get_ image_path). Here's how:

def get_image_path(instance, filename):
    dir = 'images'
    subdir = instance.get_type_display()
    sub_subdir = 'other'
    if instance.country:
        sub_subdir = instance.country.name
    name = instance.name
    extension = filename.split('.')[-1]

    return "%s/%s/%s/%s.%s" % (dir, subdir, sub_subdir, name, extension)

It works great, except in one situation: When I rename an item, change the country it's from, or change the category it's in, the image becomes a dead link because it generates a new image path without moving the orignal file.

So, the magic question:

Is there some save function in Django that I can hook into and override that will let me have the original object and the proposed values and compare them so I know where the image path was and where it will need to go (and then use this info to move/rename in code)?

+3  A: 

You probably want to look into signals:

http://docs.djangoproject.com/en/dev/topics/signals/

In particular, the django.db.models.signals.pre_save signal:

http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#pre%5Fsave

Eric Palakovich Carr
Agreed, but I would think the solution would be to use a post_save signal handler for the models that affect the path
Jiaaro
Well, either way, that gets me the file move. Updating the file path database field is a separate problem and not as important. Thank you.
Jason Champion
A: 

Thank you, that is very much a step in the right direction. I'm almost there, but there's one last roadblock. In the whole process, I'm trying to update the "picture" and "alternate_view" fields (derived from ImageField) to match the new path and it gives me a "can't set attribute" error on setting the new path (whether I refer to .name or .path in the field) Here's the code from my models.py:

def item_pre_save_handler(sender, instance, **kwargs):
    olditem = Item.objects.get(id=instance.id)
    if olditem:
        new_path = get_image_path(instance, instance.picture.path)
        new_alt_path = get_alt_image_path(instance, instance.alternate_view.path)

        instance.picture.name = new_path  # 'can't set attribute' error here.
        instance.alternate_view.name = new_alt_path

        if os.path.isfile( olditem.picture.path ):
            os.rename(olditem.picture.path, instance.picture.path)
        if os.path.isfile( olditem.alternate_view.path ):
            os.rename(olditem.alternate_view.path, instance.alternate_view.path)
        if os.path.isfile( olditem.picture.thumb_path ):
            os.rename(olditem.picture.thumb_path, instance.picture.thumb_path)
        if os.path.isfile( olditem.alternate_view.thumb_path ):
            os.rename(olditem.alternate_view.thumb_path, instance.alternate_view.thumb_path)

pre_save.connect(collectible_pre_save_handler, sender=Collectible)

So, how would I go about changing (or updating) the stored file path for an upload during the pre_save?

It almost seems like that the upload_to function that generates instance.picture.path should already have been generated before getting to the save handler, but it isn't.

Jason Champion