Django "Save as new" and keep Image fields

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)

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.


If your here in 2019 .. Updated answer for @nicolaslara This answer is Django 2+ and python 3

To get Url from Django admin we should use :

original_pk = request.resolver_match.kwargs['object_id']

and iteritems() won't work on python3 we have to use just items()

Final Code:

  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 = request.resolver_match.kwargs['object_id']
        print(original_pk)

        # 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).items():
            # if the property is an Image (don't forget to import ImageFieldFile!)
            if isinstance(getattr(original_obj, prop), ImageFieldFile):
                setattr(obj, prop, getattr(original_obj, prop))  # Copy it!
    obj.save()

Tags:

Django