Edit inline with ImageField or FileField in Django admin
Django admin lets you edit related model objects “inline”. For example when editing a Recipe
you can add/eding a group of Ingredient
models.
Core fields for edit_inline
The related model being edited inline must specify one or more “core” fields using core=True
. If the core fields are filled in, the related model is added. If the core fields are empty, the related model is removed.
This works great for normal objects with CharField
s, etc, but not so well if you want to have images or files uploaded using inline editing. If the only core field is a FileField
or ImageField
, you’ll get strange behaviour like the file/image being removed when you edit an existing model in the admin.
Using inline editing with ImageField or FileField
In a recent project I wanted to have an item with title and description and zero or more photos. The Photo
model just has an ImageField
. To make it easy to edit, I wanted the photos set to edit_inline
.
Here’s my first attempt:
class Item(models.Model): title = models.CharField(max_length=100) description = models.TextField() class Admin: pass class Photo(models.Model): item = models.ForeignKey(Item, related_name='photos', edit_inline=models.STACKED) image = models.ImageField(blank=False, upload_to='items', core=True)
Notice that the ImageField
in Photo
has core=True
to make it a core field.
This worked ok in the Django admin interface for adding a Photo
to a new Item
, but if I edited that Item
, the Photo
would be deleted.
This is a known issue (see Ticket #2534), but it’s marked as “pending design decision” and may be ignored for now since the Django admin is being rewritten to use newforms
.
In the meantime I needed a workaround.
Workaround using a different core field
Instead of having the ImageField
as a core field, we need something else. If you’ve got some other natural data, such as a caption, that would work fine.
In my case, I didn’t want to add any other fields to the interface, so I went with a BooleanField
that is not editable.
Here’s the revised Photo
model:
class Photo(models.Model): item = models.ForeignKey(Item, related_name='photos', edit_inline=models.STACKED) image = models.ImageField(blank=False, upload_to='items') keep = models.BooleanField(default=True, editable=False, core=True) def save(self): # Don't save if there is no image (since core field is always set). if not self.id and not self.image: return super(Photo, self).save()
The keep field has been added and set to be core instead of the image field. Since there is a core field and it’s not empty, the main Item
model can be edited without the Photo
models being deleted.
Don’t save the empties
The core field always has a value which means the Photo
model is told to save even when its ImageField
is empty. To prevent creating these empty objects, the Photo
model overrides save()
and checks if an image was uploaded to the ImageField
. If not, it returns without saving.
The remaining issue is that you can’t delete a Photo
using the Django admin interface. You can replace the image, but will need some other method for deleting. For me this isn’t a big problem, so the workaround solved the problem for now.
Note that this is just a workaround to the problem which hopefully will be fixed in Django at some point in the future. Ideally, the admin interface would properly handle having an ImageField
or FileField
as the only core field of the related model and optionally put a “remove” checkbox in the UI to allow removing the image/file.
Ha! It worked! Nice tip, thanks. Learning Django is a breeze, even when it isn’t.
Comment by Centipede — 24 February 2008 @ 8:36 pm
Here’s a followup with a nice way to remove related model instances as well.
http://scottbarnham.com/blog/2008/02/24/imagefield-and-edit_inline-revisited/
Comment by Scott — 24 February 2008 @ 9:17 pm
Thanks for your post. I’m new to Django and that was a very unexpected error for me.
Core keyword has been removed so this solution is no longer fit:
https://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Removedoldformsvalidatorsandrelatedcode
Comment by Nelson do Vale — 9 March 2012 @ 3:28 pm