Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django "Save as new" and keep Image fields

Tags:

django

I have a Django model with multiple ImageFields.

On the ModelAdmin class I've set save_as = True, which means The admin page has a "Save as new" button, which allows for duplicating an existing item and saving it as new.

However when this button is used, the ImageFields are not duplicated and are left blank on the new item.

Looking at the POST request, I see that these fields are blank in the post data.

I've thought about overriding the Model class' save method, and copying the images from the old object by myself. But as far as I could figure out, I have no way to tell that the object is saved "as new". I also don't seem to have the ID of the old Item so I cannot get the old Images from it.

Is there a way to get these image fields to be duplicated as well?

Edit: Added Example code by request.

Created a minimalistic app with only one model. Verified problem still occurs.

Sample models.py:

from django.db import models

class Person(models.Model):
    face_image = models.ImageField(upload_to='images', 
                                   null=False, 
                                   blank=True)

Sample admin.py:

from django.contrib import admin
from testapp.models import Person

class PersonAdmin(admin.ModelAdmin):
    save_as = True

admin.site.register(Person, PersonAdmin)
like image 354
Zappatta Avatar asked Sep 02 '13 16:09

Zappatta


2 Answers

Building up on this response, here's a more generic way of achieving the same result:

from django.core.urlresolvers import resolve
from django.db.models.fields.files import FieldFile

class PersonAdmin(admin.ModelAdmin):
    save_as = True

    def save_model(self, request, obj, form, change):
        # Django always sends this when "Save as new is clicked"
        if '_saveasnew' in request.POST:
            # Get the ID from the admin URL
            original_pk = resolve(request.path).args[0]
            # Get the original object
            original_obj = obj._meta.concrete_model.objects.get(id=original_pk)

            # Iterate through all it's properties
            for prop, value in vars(original_obj).iteritems():
                # if the property is an Image (don't forget to import ImageFieldFile!)
                if isinstance(getattr(original_obj, prop), FieldFile):
                    setattr(obj,prop,getattr(original_obj, prop)) # Copy it!
        obj.save()

This should work with any model and any file type. It also doesn't require editing the form or the template. This is a workaround that should not be needed once the pull request gets merged: https://github.com/django/django/pull/2246.

like image 196
nicolaslara Avatar answered Sep 20 '22 17:09

nicolaslara


I've managed to find some workaround:

I've overridden the original admin form (see here) to get it also include the old Model's ID in "save as new" POST request. I've did it by creating a special admin for of that model, and adding inside it a hidden input:

<input type="hidden" name="my_objectid" value="{{ object_id }}">

afterwards I've made the ModelAdmin class load that specific html. Then I overriden the AdminModel class' save_model method so it would copy the images as well.

So the new admin.py should look like this:

from django.contrib import admin
from testapp.models import Person

from django.db.models.fields.files import ImageFieldFile #added to be used later

class PersonAdmin(admin.ModelAdmin):
    save_as=True
    change_form_template = 'admin/person_change_form.html';
    def save_model(self, request, obj, form, change):       

        if '_saveasnew' in request.POST: #Django always sends this when "Save as new is clicked"
            origObjId = request.POST['my_objectid']; #Get the ID that is new posted after overriding the form. 
            originalPerson = Person.objects.get(id=origObjId); #Use the Id to get the old object
            for prop, value in vars(originalPerson).iteritems(): #iterate through all it's properties
                if isinstance(getattr(originalPerson,prop), ImageFieldFile): #if the property is an Image (don't forget to import ImageFieldFile!)
                    setattr(obj,prop,getattr(originalPerson,prop)) #Copy it!

        obj.save()

admin.site.register(Person, PersonAdmin)
like image 38
Zappatta Avatar answered Sep 21 '22 17:09

Zappatta