June 30, 2008
Edit Inline Support for Generic Relations
I've recently (past week or so) been digging into the newforms-admin branch of django. I am really looking forward to this code line getting merged into trunk as there are tons of great work in this branch. I especially like the separation of the admin code from the models.
Well, I'll get to my point.
I needed to be able to edit child records in the admin that were children through the use of GenericRelations. I was told about a solution on Google Code called django-genericadmin, but it seemed dead and I could not get it to work.
The thing that seemed the closest to working was a patch on ticket 4667 by Honza Kral and a variation on this patch in the Django Snippet 765. Neither of these worked for r7771 of branches/newforms- admin.
I put the 765 snippet in a file called
generic.py in the root of the django
project I was working on and fiddled with it until I got it working. I ended
up needing to change an import, fix some variable names, and change the
argument list order in one of the init methods. I also added the
can_delete options to the
After I got it working isolated in
generic.py, I then added the code to the
django/contrib/admin/options.py file and created a new patch (after testing
locally of course). This patch has since been added to ticket
4667. I also posted the full
version that I had running in
generic.py in snippet
832 in case someone wants to use
it without patching their newforms-admin branch.
Example of How to Use
In order to get a sense for how to make use of this functionality, imagine you have a songs app that you use to model and record details about songs, you have built this generically so you can reuse it wisely in a number of different projects. One project in particular has a need to to reference song data.
You are building a home media inventory system and decide you want to catalog both your albums as well as your DVD collection, however, you want the ability to record what songs exist on both your albums as well as your DVDs as you are a fanatic about soundtracks.
So you have the following models (abbreviated for clarity):
# Media Collection App class Album(models.Model): ... class DVD(models.Model): ... # Song App class Song(models.Model): ...
Since you want to enable your
Song model to relate to two other models
generically, you decide to use Generic Relations like so:
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic class Song(models.Model): ... content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey()
and then on the models that you want to have 0 to Many child records that are songs:
from myproject.song.models import Song from django.contrib.contenttypes import generic class Album(models.Model): ... songs = generic.GenericRelation(Song) class DVD(models.Model): ... songs = generic.GenericRelation(Song)
Now that's all you need to do if you didn't care about editing the data in the
admin, however, since that is not the point of the article, here is what you
need to do in your
admin.py in the app that contains
... from myproject.song.models import Song from myproject.generic import GenericTabularInline ### OR ### ### from django.contrib.admin.options import GenericTabularInline class SongInline(GenericTabularInline): model = Song extra = 2 ct_field_name = 'content_type' id_field_name = 'object_id' class AlbumOptions(admin.ModelAdmin): model = Album inlines = (SongInline,) class DVDOptions(admin.ModelAdmin): model = DVD inlines = (SongInline,) admin.site.register(Album, AlbumOptions) admin.site.register(DVD, DVDOptions)
Now you should be able to add/remove songs when editing an
Update: Fixed typo in code sample above in setting the GenericForeignKey() -- thanks FunkyBob!