diff -r 2d5cd6f1136e django/contrib/admin/options.py
--- a/django/contrib/admin/options.py	Fri Nov 13 16:43:57 2009 +1030
+++ b/django/contrib/admin/options.py	Wed Nov 18 10:50:05 2009 +1030
@@ -830,6 +830,17 @@
                 formset = FormSet(request.POST, request.FILES,
                                   instance=new_object, prefix=prefix)
                 formsets.append(formset)
+                for inline in self.inline_instances:
+                    # If this is the inline that matches this formset, and
+                    # we have some nested inlines to deal with, then we need
+                    # to get the relevant formset for each of the forms in
+                    # the current formset.
+                    if inline.inlines and inline.model == formset.model:
+                        for nested in inline.inline_instances:
+                            for the_form in formset.forms:
+                                InlineFormSet = nested.get_formset(request, the_form.instance)
+                                prefix = "%s-%s" % (the_form.prefix, InlineFormSet.get_default_prefix())
+                                formsets.append(InlineFormSet(request.POST, request.FILES, instance=the_form.instance, prefix=prefix))
 
             if all_valid(formsets) and form_validated:
                 self.save_model(request, new_object, form, change=True)
@@ -859,6 +870,14 @@
         for inline, formset in zip(self.inline_instances, formsets):
             fieldsets = list(inline.get_fieldsets(request, obj))
             inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, fieldsets)
+            if inline.inlines:
+                for form in formset.forms:
+                    if form.instance.pk:
+                        instance = form.instance
+                    else:
+                        instance = None
+                    form.inlines = inline.get_inlines(request, instance, prefix=form.prefix)
+                inline_admin_formset.inlines = inline.get_inlines(request)
             inline_admin_formsets.append(inline_admin_formset)
             media = media + inline_admin_formset.media
 
@@ -1119,6 +1138,7 @@
     template = None
     verbose_name = None
     verbose_name_plural = None
+    inlines = []
 
     def __init__(self, parent_model, admin_site):
         self.admin_site = admin_site
@@ -1129,6 +1149,10 @@
             self.verbose_name = self.model._meta.verbose_name
         if self.verbose_name_plural is None:
             self.verbose_name_plural = self.model._meta.verbose_name_plural
+        self.inline_instances = []
+        for inline_class in self.inlines:
+            inline_instance = inline_class(self.model, self.admin_site)
+            self.inline_instances.append(inline_instance)
 
     def _media(self):
         from django.conf import settings
@@ -1171,6 +1195,18 @@
         form = self.get_formset(request).form
         return [(None, {'fields': form.base_fields.keys()})]
 
+    def get_inlines(self, request, obj=None, prefix=None):
+        nested_inlines = []
+        for inline in self.inline_instances:
+            FormSet = inline.get_formset(request, obj)
+            prefix = "%s-%s" % (prefix, FormSet.get_default_prefix())
+            formset = FormSet(instance=obj, prefix=prefix)
+            fieldsets = list(inline.get_fieldsets(request, obj))
+            nested_inline = helpers.InlineAdminFormSet(inline, formset, fieldsets)
+            nested_inlines.append(nested_inline)
+        return nested_inlines
+            
+
 class StackedInline(InlineModelAdmin):
     template = 'admin/edit_inline/stacked.html'
 
diff -r 2d5cd6f1136e django/contrib/admin/templates/admin/edit_inline/nested.html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/django/contrib/admin/templates/admin/edit_inline/nested.html	Wed Nov 18 10:50:05 2009 +1030
@@ -0,0 +1,48 @@
+<td>
+  {{ nested.formset.management_form }}
+  <table>
+    <thead>
+      <tr>
+        {% for field in nested.fields %}
+          <th {% if forloop.first %}colspan="2"{% endif %}>{{ field.label|capfirst }}</th>
+        {% endfor %}
+        {% if nested.formset.can_delete %}
+          <th>Delete?</th>
+        {% endif %}
+      </tr>
+    </thead>
+    
+    <tbody>
+      {% for formset in nested %}
+      {% if formset.form.non_field_errors %}
+      <tr><td colspan="{{ formset.field_count }}">
+        {{ form.form.non_field_errors }}
+      </td></tr>
+      {% endif %}
+        <tr class="{% if formset.original %}has_original{% endif %}">
+          <td class="original">
+            {% if formset.original %}<p style="position:relative;">
+              {{ formset.original }}
+            </p>{% endif %}
+            {% if formset.has_auto_field %}
+              {{ formset.pk_field.field }}
+            {% endif %}{{ formset.fk_field.field }}
+          </td>
+          {% for fieldset in formset %}
+              {% for line in fieldset %}
+                {% for field in line %}
+                  <td class="{{ field.field.name }}">
+                    {{ field.field.errors.as_ul}}
+                    {{ field.field }}
+                  </td>
+                {% endfor %}
+              {% endfor %} 
+          {% endfor %}
+          {% if formset.original and nested.formset.can_delete %}
+            <td class="delete">{{ formset.deletion_field.field }}</td>
+          {% endif %}
+        </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+</td>
\ No newline at end of file
diff -r 2d5cd6f1136e django/contrib/admin/templates/admin/edit_inline/tabular.html
--- a/django/contrib/admin/templates/admin/edit_inline/tabular.html	Fri Nov 13 16:43:57 2009 +1030
+++ b/django/contrib/admin/templates/admin/edit_inline/tabular.html	Wed Nov 18 10:50:05 2009 +1030
@@ -13,6 +13,11 @@
         {% endif %}
      {% endfor %}
      {% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %}
+     {% if inline_admin_formset.inlines %}
+      {% for nested in inline_admin_formset.inlines %}
+        <th>{{ nested.opts.verbose_name_plural|capfirst }}</th>
+      {% endfor %}
+     {% endif %}
      </tr></thead>
 
      <tbody>
@@ -21,7 +26,7 @@
         <tr><td colspan="{{ inline_admin_form.field_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
         {% endif %}
         <tr class="{% cycle row1,row2 %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}">
-
+          
         <td class="original">
           {% if inline_admin_form.original or inline_admin_form.show_url %}<p>
           {% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
@@ -54,9 +59,13 @@
         {% if inline_admin_formset.formset.can_delete %}
           <td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>
         {% endif %}
-
+        
+        {% if inline_admin_formset.inlines %}
+          {% for nested in inline_admin_form.form.inlines %}
+            {% include 'admin/edit_inline/nested.html' %}
+          {% endfor %}
+        {% endif %}
         </tr>
-
      {% endfor %}
      </tbody>
    </table>
diff -r 2d5cd6f1136e django/contrib/admin/validation.py
--- a/django/contrib/admin/validation.py	Fri Nov 13 16:43:57 2009 +1030
+++ b/django/contrib/admin/validation.py	Wed Nov 18 10:50:05 2009 +1030
@@ -177,6 +177,23 @@
             raise ImproperlyConfigured("%s cannot exclude the field "
                     "'%s' - this is the foreign key to the parent model "
                     "%s." % (cls.__name__, fk.name, parent_model.__name__))
+    
+    # nested inlines
+    # inlines = []
+    if hasattr(cls, 'inlines'):
+        check_isseq(cls, 'inlines', cls.inlines)
+        for idx, inline in enumerate(cls.inlines):
+            if not issubclass(inline, BaseModelAdmin):
+                raise ImproperlyConfigured("'%s.inlines[%d]' does not inherit"
+                    " from BaseModelAdmin." % (cls.__name__, idx))
+            if not inline.model:
+                raise ImproperlyConfigured("'model' is a required attribute "
+                    "of '%s.inlines[%d]'." % (cls.__name__, idx))
+            if not issubclass(inline.model, models.Model):
+                raise ImproperlyConfigured("'%s.inlines[%s].model' does not "
+                    "inherit from models.Model." % (cls.__name__, idx))
+            validate_base(inline, inline.model)
+            validate_inline(inline, cls, cls.model)
 
 def validate_base(cls, model):
     opts = model._meta
