commit 6b7d2a97155dd8fd02c9280a50678c169954eb88
Author: Jakub Vysoky <jakub@borka.cz>
Date:   Sat Jul 12 00:15:00 2008 +0200

    Support for edit_inline for generic relations
    
    http://code.djangoproject.com/ticket/4667

diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py
index e91be70..6c867be 100644
--- a/django/contrib/contenttypes/generic.py
+++ b/django/contrib/contenttypes/generic.py
@@ -281,3 +281,69 @@ class GenericRel(ManyToManyRel):
         self.multiple = True
         assert not (self.raw_id_admin and self.filter_interface), \
             "Generic relations may not use both raw_id_admin and filter_interface"
+
+from django.newforms.models import BaseModelFormSet, modelformset_factory, save_instance
+from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
+
+class GenericInlineFormset(BaseModelFormSet):
+    """A formset for child objects related to a parent."""
+    def __init__(self, data=None, files=None, instance=None, save_as_new=None):
+        self.instance = instance
+        self.rel_name = '-'.join( ( self.model._meta.app_label, self.model._meta.object_name.lower(), self.ct.name,  self.obj_id.name ) )
+        super(GenericInlineFormset, self).__init__(queryset=self.get_queryset(), data=data, files=files, prefix=self.rel_name)
+
+    def get_queryset(self):
+        # This import is done here to avoid circular import importing this module
+        from django.contrib.contenttypes.models import ContentType
+        if self.instance is None:
+            return []
+        return self.model._default_manager.filter( **{ 
+                self.ct.name : ContentType.objects.get_for_model(self.instance),
+                self.obj_id.name : self.instance.pk
+            })
+
+    def save_new(self, form, commit=True):
+        # This import is done here to avoid circular import importing this module
+        from django.contrib.contenttypes.models import ContentType
+        kwargs = {
+            self.ct.get_attname(): ContentType.objects.get_for_model(self.instance).pk,
+            self.obj_id.get_attname(): self.instance.pk,
+        }
+        new_obj = self.model(**kwargs)
+        return save_instance(form, new_obj, commit=commit)
+
+class GenericInlineModelAdmin(InlineModelAdmin):
+    ct_field_name = None
+    id_field_name = None
+    formset = GenericInlineFormset
+
+    def get_formset( self, request, obj=None ):
+        from django.db.models import ForeignKey
+        from django.contrib.contenttypes.models import ContentType
+        if self.declared_fieldsets:
+            fields = flatten_fieldsets(self.declared_fieldsets)
+        else:
+            fields = None
+
+        opts = self.model._meta
+        # if there is no field called `ct_field_name` let the exception propagate
+        ct = opts.get_field(self.ct_field_name)
+        if not isinstance(ct, ForeignKey) or ct.rel.to != ContentType:
+            raise Exception("fk_name '%s' is not a ForeignKey to ContentType" % (self.ct_field_name))
+        obj_id = opts.get_field(self.id_field_name) # let the exception propagate
+
+        FormSet = modelformset_factory(self.model, form=self.form,
+                                        formfield_callback=self.formfield_for_dbfield,
+                                        formset=self.formset,
+                                        extra=self.extra, can_delete=True, can_order=False,
+                                        fields=fields, exclude=[ct.name, obj_id.name] )
+        FormSet.ct = ct
+        FormSet.obj_id = obj_id
+        return FormSet
+
+class GenericStackedInline(GenericInlineModelAdmin):
+    template = 'admin/edit_inline/stacked.html'
+
+class GenericTabularInline(GenericInlineModelAdmin):
+    template = 'admin/edit_inline/tabular.html'
+
