Index: django/contrib/admin/options.py
===================================================================
--- django/contrib/admin/options.py	(revision 7789)
+++ django/contrib/admin/options.py	(working copy)
@@ -2,7 +2,8 @@
 from django import newforms as forms
 from django.newforms.formsets import all_valid
 from django.newforms.models import modelform_factory, inlineformset_factory
-from django.newforms.models import BaseInlineFormset
+from django.newforms.models import BaseInlineFormset, BaseModelFormSet
+from django.newforms.models import save_instance, modelformset_factory
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.admin import widgets
 from django.contrib.admin.util import get_deleted_objects
@@ -823,3 +824,66 @@
     def ordering_field(self):
         from django.newforms.formsets import ORDERING_FIELD_NAME
         return AdminField(self.form, ORDERING_FIELD_NAME, False)
+
+
+class GenericInlineFormset(BaseModelFormSet):
+    """A formset for child objects related to a parent."""
+    def __init__(self, data=None, files=None, instance=None, save_as_new=False):
+        self.save_as_new = save_as_new
+        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):
+        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):
+        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
+    can_delete = True
+    can_order = True
+
+    def get_formset( self, request, obj=None ):
+        if self.declared_fieldsets:
+            fields = flatten_fieldsets(self.declared_fieldsets)
+        else:
+            fields = None
+  
+        opts = self.model._meta
+        ct = opts.get_field(self.ct_field_name)
+        if not isinstance(ct, models.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, 
+                                        fields=fields, 
+                                        can_delete=self.can_delete, 
+                                        can_order=self.can_order,
+                                        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'
