diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index c87d903..82e0b70 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -108,10 +108,10 @@ class BaseModelAdmin(object):
 
         # If we've got overrides for the formfield defined, use 'em. **kwargs
         # passed to formfield_for_dbfield override the defaults.
-        for klass in db_field.__class__.mro(): 
-            if klass in self.formfield_overrides: 
-                kwargs = dict(self.formfield_overrides[klass], **kwargs) 
-                return db_field.formfield(**kwargs) 
+        for klass in db_field.__class__.mro():
+            if klass in self.formfield_overrides:
+                kwargs = dict(self.formfield_overrides[klass], **kwargs)
+                return db_field.formfield(**kwargs)
 
         # For any other type of field, just call its formfield() method.
         return db_field.formfield(**kwargs)
@@ -336,10 +336,12 @@ class ModelAdmin(BaseModelAdmin):
             exclude = []
         else:
             exclude = list(self.exclude)
+        # if exclude is an empty list we pass None to be consistant with the
+        # default on modelform_factory
         defaults = {
             "form": self.form,
             "fields": fields,
-            "exclude": exclude + kwargs.get("exclude", []),
+            "exclude": (exclude + kwargs.get("exclude", [])) or None,
             "formfield_callback": curry(self.formfield_for_dbfield, request=request),
         }
         defaults.update(kwargs)
@@ -437,26 +439,26 @@ class ModelAdmin(BaseModelAdmin):
         # want *any* actions enabled on this page.
         if self.actions is None:
             return []
-            
+
         actions = []
-        
+
         # Gather actions from the admin site first
         for (name, func) in self.admin_site.actions:
             description = getattr(func, 'short_description', name.replace('_', ' '))
             actions.append((func, name, description))
-        
-        # Then gather them from the model admin and all parent classes, 
+
+        # Then gather them from the model admin and all parent classes,
         # starting with self and working back up.
         for klass in self.__class__.mro()[::-1]:
             class_actions = getattr(klass, 'actions', [])
             # Avoid trying to iterate over None
             if not class_actions:
-                continue 
+                continue
             actions.extend([self.get_action(action) for action in class_actions])
-        
+
         # get_action might have returned None, so filter any of those out.
         actions = filter(None, actions)
-        
+
         # Convert the actions into a SortedDict keyed by name
         # and sorted by description.
         actions.sort(lambda a,b: cmp(a[2].lower(), b[2].lower()))
@@ -464,7 +466,7 @@ class ModelAdmin(BaseModelAdmin):
             (name, (func, name, desc))
             for func, name, desc in actions
         ])
-        
+
         return actions
 
     def get_action_choices(self, request, default_choices=BLANK_CHOICE_DASH):
@@ -488,20 +490,20 @@ class ModelAdmin(BaseModelAdmin):
         if callable(action):
             func = action
             action = action.__name__
-            
+
         # Next, look for a method. Grab it off self.__class__ to get an unbound
         # method instead of a bound one; this ensures that the calling
         # conventions are the same for functions and methods.
         elif hasattr(self.__class__, action):
             func = getattr(self.__class__, action)
-        
+
         # Finally, look for a named method on the admin site
         else:
             try:
                 func = self.admin_site.get_action(action)
             except KeyError:
                 return None
-            
+
         if hasattr(func, 'short_description'):
             description = func.short_description
         else:
@@ -664,7 +666,7 @@ class ModelAdmin(BaseModelAdmin):
         data = request.POST.copy()
         data.pop(helpers.ACTION_CHECKBOX_NAME, None)
         data.pop("index", None)
-        
+
         # Use the action whose button was pushed
         try:
             data.update({'action': data.getlist('action')[action_index]})
@@ -673,7 +675,7 @@ class ModelAdmin(BaseModelAdmin):
             # POST data, so by deleting action it'll fail the validation check
             # below. So no need to do anything here
             pass
-        
+
         action_form = self.action_form(data, auto_id=None)
         action_form.fields['action'].choices = self.get_action_choices(request)
 
@@ -877,10 +879,10 @@ class ModelAdmin(BaseModelAdmin):
         app_label = opts.app_label
         if not self.has_change_permission(request, None):
             raise PermissionDenied
-        
+
         # Check actions to see if any are available on this changelist
         actions = self.get_actions(request)
-        
+
         # Remove action checkboxes if there aren't any actions available.
         list_display = list(self.list_display)
         if not actions:
@@ -888,7 +890,7 @@ class ModelAdmin(BaseModelAdmin):
                 list_display.remove('action_checkbox')
             except ValueError:
                 pass
-        
+
         try:
             cl = ChangeList(request, self.model, list_display, self.list_display_links, self.list_filter,
                 self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page, self.list_editable, self)
@@ -901,7 +903,7 @@ class ModelAdmin(BaseModelAdmin):
             if ERROR_FLAG in request.GET.keys():
                 return render_to_response('admin/invalid_setup.html', {'title': _('Database error')})
             return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')
-                
+
         # If the request was POSTed, this might be a bulk action or a bulk edit.
         # Try to look up an action first, but if this isn't an action the POST
         # will fall through to the bulk edit check, below.
diff --git a/django/forms/models.py b/django/forms/models.py
index 010d3bf..8d59dfd 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -345,13 +345,18 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
                        formfield_callback=lambda f: f.formfield()):
     # HACK: we should be able to construct a ModelForm without creating
     # and passing in a temporary inner class
-    class Meta:
-        pass
-    setattr(Meta, 'model', model)
-    setattr(Meta, 'fields', fields)
-    setattr(Meta, 'exclude', exclude)
+    attrs = {}
+    if fields is not None:
+        attrs['fields'] = fields
+    if exclude is not None:
+        attrs['exclude'] = exclude
+    attrs['model'] = model
+    parent = (object,)
+    if hasattr(form, 'Meta'):
+        parent = (form.Meta, object)
+    meta = type('Meta', parent, attrs)
     class_name = model.__name__ + 'Form'
-    return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
+    return ModelFormMetaclass(class_name, (form,), {'Meta': meta,
                               'formfield_callback': formfield_callback})
 
 
diff --git a/tests/regressiontests/modeladmin/models.py b/tests/regressiontests/modeladmin/models.py
index 3a7d3f0..eeb2563 100644
--- a/tests/regressiontests/modeladmin/models.py
+++ b/tests/regressiontests/modeladmin/models.py
@@ -8,7 +8,7 @@ class Band(models.Model):
     name = models.CharField(max_length=100)
     bio = models.TextField()
     sign_date = models.DateField()
-    
+
     def __unicode__(self):
         return self.name
 
@@ -118,27 +118,27 @@ displayed because you forgot to add it to fields/fielsets
 
 # Using `exclude`.
 
->>> class BandAdmin(ModelAdmin): 
-...     exclude = ['bio'] 
->>> ma = BandAdmin(Band, site) 
->>> ma.get_form(request).base_fields.keys() 
+>>> class BandAdmin(ModelAdmin):
+...     exclude = ['bio']
+>>> ma = BandAdmin(Band, site)
+>>> ma.get_form(request).base_fields.keys()
 ['name', 'sign_date']
 
 # You can also pass a tuple to `exclude`.
- 
->>> class BandAdmin(ModelAdmin): 
-...     exclude = ('bio',) 
->>> ma = BandAdmin(Band, site) 
->>> ma.get_form(request).base_fields.keys() 
+
+>>> class BandAdmin(ModelAdmin):
+...     exclude = ('bio',)
+>>> ma = BandAdmin(Band, site)
+>>> ma.get_form(request).base_fields.keys()
 ['name', 'sign_date']
- 
+
 # Using `fields` and `exclude`.
 
->>> class BandAdmin(ModelAdmin): 
-...     fields = ['name', 'bio'] 
-...     exclude = ['bio'] 
->>> ma = BandAdmin(Band, site) 
->>> ma.get_form(request).base_fields.keys() 
+>>> class BandAdmin(ModelAdmin):
+...     fields = ['name', 'bio']
+...     exclude = ['bio']
+>>> ma = BandAdmin(Band, site)
+>>> ma.get_form(request).base_fields.keys()
 ['name']
 
 If we specify a form, it should use it allowing custom validation to work
@@ -147,7 +147,7 @@ properly. This won't, however, break any of the admin widgets or media.
 >>> from django import forms
 >>> class AdminBandForm(forms.ModelForm):
 ...     delete = forms.BooleanField()
-...     
+...
 ...     class Meta:
 ...         model = Band
 
@@ -262,6 +262,18 @@ blank=True for the model field.  Finally, the widget should have the
 >>> list(cmafa.base_fields['transport'].widget.choices)
 [('', u'None'), (1, 'Plane'), (2, 'Train'), (3, 'Bus')]
 
+>>> class AdminConcertForm(forms.ModelForm):
+...     class Meta:
+...         model = Concert
+...         exclude = ('transport',)
+
+>>> class ConcertAdmin(ModelAdmin):
+...     form = AdminConcertForm
+
+>>> ma = ConcertAdmin(Concert, site)
+>>> ma.get_form(request).base_fields.keys()
+['main_band', 'opening_band', 'day']
+
 >>> band.delete()
 
 # ModelAdmin Option Validation ################################################
@@ -371,11 +383,11 @@ Traceback (most recent call last):
 ...
 ImproperlyConfigured: Both fieldsets and fields are specified in ValidationTestModelAdmin.
 
->>> class ValidationTestModelAdmin(ModelAdmin): 
-...     fieldsets = [(None, {'fields': ['name', 'name']})] 
->>> validate(ValidationTestModelAdmin, ValidationTestModel) 
-Traceback (most recent call last): 
-... 
+>>> class ValidationTestModelAdmin(ModelAdmin):
+...     fieldsets = [(None, {'fields': ['name', 'name']})]
+>>> validate(ValidationTestModelAdmin, ValidationTestModel)
+Traceback (most recent call last):
+...
 ImproperlyConfigured: There are duplicate field(s) in ValidationTestModelAdmin.fieldsets
 
 >>> class ValidationTestModelAdmin(ModelAdmin):
