commit 5b9f454eef18f63c85c35e3f2ee903723199b508
Author: Honza Král <Honza.Kral@gmail.com>
Date:   Tue Oct 2 18:18:24 2007 +0200

    Added generic edit inline patch
    
    http://code.djangoproject.com/ticket/4667

diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 9018472..ef98bc0 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -694,6 +694,7 @@ class InlineModelAdmin(BaseModelAdmin):
     template = None
     verbose_name = None
     verbose_name_plural = None
+    formset = None
 
     def __init__(self, parent_model, admin_site):
         self.admin_site = admin_site
@@ -711,7 +712,8 @@ class InlineModelAdmin(BaseModelAdmin):
             fields = flatten_fieldsets(self.declared_fieldsets)
         else:
             fields = None
-        return forms.inline_formset(self.parent_model, self.model, fk_name=self.fk_name, fields=fields, formfield_callback=self.formfield_for_dbfield, extra=self.extra)
+        return forms.inline_formset(self.parent_model, self.model, fk_name=self.fk_name, fields=fields,
+                    formfield_callback=self.formfield_for_dbfield, extra=self.extra, formset=self.formset)
 
     def formset_change(self, request, obj):
         """Returns an InlineFormSet class for use in admin change views."""
@@ -719,7 +721,8 @@ class InlineModelAdmin(BaseModelAdmin):
             fields = flatten_fieldsets(self.declared_fieldsets)
         else:
             fields = None
-        return forms.inline_formset(self.parent_model, self.model, fk_name=self.fk_name, fields=fields, formfield_callback=self.formfield_for_dbfield, extra=self.extra)
+        return forms.inline_formset(self.parent_model, self.model, fk_name=self.fk_name, fields=fields,
+                    formfield_callback=self.formfield_for_dbfield, extra=self.extra, formset=self.formset)
 
     def fieldsets_add(self, request):
         if self.declared_fieldsets:
diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py
index b738a26..0612ffc 100644
--- a/django/contrib/contenttypes/generic.py
+++ b/django/contrib/contenttypes/generic.py
@@ -260,3 +260,52 @@ 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 InlineFormset, save_instance
+class GenericInlineFormset(InlineFormset):
+
+    @classmethod
+    def add_fk(cls, parent_model, model, fk_name):
+        from django.template.defaultfilters import slugify
+        assert ':' in fk_name, "Supply valid content_type:object_id as fk_name."
+
+        opts = model._meta
+        ct_field_name, id_field_name = fk_name.split(':')
+        ct = opts.get_field( ct_field_name )
+        obj_id = opts.get_field( id_field_name )
+        # HACK: remove the ForeignKey to the parent from every form
+        # This should be done a line above before we pass 'fields' to formset_for_model
+        # an 'omit' argument would be very handy here
+        try:
+            del cls.form_class.base_fields[ct.name]
+            del cls.form_class.base_fields[obj_id.name]
+        except KeyError:
+            pass
+
+        cls.parent_model = parent_model
+        cls.model = model
+        cls.ct_field_name = ct_field_name
+        cls.id_field_name = id_field_name
+        cls.ct = ct
+        cls.obj_id = obj_id
+        cls.rel_name = slugify(fk_name)
+
+    def get_inline_objects(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_field_name : ContentType.objects.get_for_model(self.instance),
+                self.id_field_name : self.instance._get_pk_val() 
+            })
+
+    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).id,
+            self.obj_id.get_attname(): self.instance._get_pk_val(),
+        }
+        new_obj = self.model(**kwargs)
+        return save_instance(form, new_obj, commit=commit)
diff --git a/django/newforms/models.py b/django/newforms/models.py
index 5c4dc08..61b8dc7 100644
--- a/django/newforms/models.py
+++ b/django/newforms/models.py
@@ -295,10 +295,8 @@ def formset_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formf
 class InlineFormset(BaseModelFormSet):
     """A formset for child objects related to a parent."""
     def __init__(self, instance=None, data=None, files=None):
-        from django.db.models.fields.related import RelatedObject
         self.instance = instance
         # is there a better way to get the object descriptor?
-        self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
         super(InlineFormset, self).__init__(data, files, instances=self.get_inline_objects(), prefix=self.rel_name)
 
     def get_inline_objects(self):
@@ -311,45 +309,53 @@ class InlineFormset(BaseModelFormSet):
         new_obj = self.model(**kwargs)
         return save_instance(form, new_obj, commit=commit)
 
-def inline_formset(parent_model, model, fk_name=None, fields=None, extra=3, orderable=False, deletable=True, formfield_callback=lambda f: f.formfield()):
+    @classmethod
+    def add_fk(cls, parent_model, model, fk_name):
+        from django.db.models import ForeignKey 
+        from django.db.models.fields.related import RelatedObject
+        opts = model._meta
+        # figure out what the ForeignKey from model to parent_model is
+        if fk_name is None:
+            fks_to_parent = [f for f in opts.fields if isinstance(f, ForeignKey) and f.rel.to == parent_model]
+            if len(fks_to_parent) == 1:
+                fk = fks_to_parent[0]
+            elif len(fks_to_parent) == 0:
+                raise Exception("%s has no ForeignKey to %s" % (model, parent_model))
+            else:
+                raise Exception("%s has more than 1 ForeignKey to %s" % (model, parent_model))
+        else:
+            fks_to_parent = [f for f in opts.fields if f.name == fk_name]
+            if len(fks_to_parent) == 1:
+                fk = fks_to_parent[0]
+                if not isinstance(fk, ForeignKey) or fk.rel.to != parent_model:
+                    raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
+            elif len(fks_to_parent) == 0:
+                raise Exception("%s has no field named '%s'" % (model, fk_name))
+        # HACK: remove the ForeignKey to the parent from every form
+        # This should be done a line above before we pass 'fields' to formset_for_model
+        # an 'omit' argument would be very handy here
+        try:
+            del cls.form_class.base_fields[fk.name]
+        except KeyError:
+            pass
+
+        cls.parent_model = parent_model
+        cls.fk_name = fk.name
+        cls.rel_name = RelatedObject(fk.rel.to, model, fk).get_accessor_name()
+        cls.fk = fk
+
+def inline_formset(parent_model, model, fk_name=None, fields=None, extra=3, orderable=False,
+                    deletable=True, formfield_callback=lambda f: f.formfield(), formset=None):
     """
     Returns an ``InlineFormset`` for the given kwargs.
 
     You must provide ``fk_name`` if ``model`` has more than one ``ForeignKey``
     to ``parent_model``.
     """
-    from django.db.models import ForeignKey
-    opts = model._meta
-    # figure out what the ForeignKey from model to parent_model is
-    if fk_name is None:
-        fks_to_parent = [f for f in opts.fields if isinstance(f, ForeignKey) and f.rel.to == parent_model]
-        if len(fks_to_parent) == 1:
-            fk = fks_to_parent[0]
-        elif len(fks_to_parent) == 0:
-            raise Exception("%s has no ForeignKey to %s" % (model, parent_model))
-        else:
-            raise Exception("%s has more than 1 ForeignKey to %s" % (model, parent_model))
-    else:
-        fks_to_parent = [f for f in opts.fields if f.name == fk_name]
-        if len(fks_to_parent) == 1:
-            fk = fks_to_parent[0]
-            if not isinstance(fk, ForeignKey) or fk.rel.to != parent_model:
-                raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
-        elif len(fks_to_parent) == 0:
-            raise Exception("%s has no field named '%s'" % (model, fk_name))
     # let the formset handle object deletion by default
-    FormSet = formset_for_model(model, formset=InlineFormset, fields=fields,
+    FormSet = formset_for_model(model, formset=formset or InlineFormset, fields=fields,
                                 formfield_callback=formfield_callback,
                                 extra=extra, orderable=orderable,
                                 deletable=deletable)
-    # HACK: remove the ForeignKey to the parent from every form
-    # This should be done a line above before we pass 'fields' to formset_for_model
-    # an 'omit' argument would be very handy here
-    try:
-        del FormSet.form_class.base_fields[fk.name]
-    except KeyError:
-        pass
-    FormSet.parent_model = parent_model
-    FormSet.fk_name = fk.name
-    FormSet.fk = fk
+    FormSet.add_fk(parent_model, model, fk_name)
     return FormSet
