diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index c055f4e..07c23b1 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -154,9 +154,9 @@ class BaseModelAdmin(object):
         """
         Get a form Field for a ManyToManyField.
         """
-        # If it uses an intermediary model that isn't auto created, don't show
-        # a field in admin.
-        if not db_field.rel.through._meta.auto_created:
+        # If it uses an intermediary model that isn't insertable with just the
+        # related models, don't show a field in admin.
+        if not db_field.rel.through._meta.insertable_with_only_relationships:
             return None
 
         if db_field.name in self.raw_id_fields:
diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py
index 726da65..937fcfd 100644
--- a/django/contrib/admin/validation.py
+++ b/django/contrib/admin/validation.py
@@ -197,10 +197,10 @@ def validate_base(cls, model):
         for field in cls.fields:
             check_formfield(cls, model, opts, 'fields', field)
             f = get_field(cls, model, opts, 'fields', field)
-            if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created:
+            if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.insertable_with_only_relationships:
                 raise ImproperlyConfigured("'%s.fields' can't include the ManyToManyField "
-                    "field '%s' because '%s' manually specifies "
-                    "a 'through' model." % (cls.__name__, field, field))
+                    "field '%s' because '%s' has a 'through' model that is not marked "
+                    "as safe to insert." % (cls.__name__, field, field))
         if cls.fieldsets:
             raise ImproperlyConfigured('Both fieldsets and fields are specified in %s.' % cls.__name__)
         if len(cls.fields) > len(set(cls.fields)):
@@ -228,11 +228,11 @@ def validate_base(cls, model):
                     check_formfield(cls, model, opts, "fieldsets[%d][1]['fields']" % idx, field)
                     try:
                         f = opts.get_field(field)
-                        if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created:
+                        if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.insertable_with_only_relationships:
                             raise ImproperlyConfigured("'%s.fieldsets[%d][1]['fields']' "
                                 "can't include the ManyToManyField field '%s' because "
-                                "'%s' manually specifies a 'through' model." % (
-                                    cls.__name__, idx, field, field))
+                                "'%s' has a 'through' model that is not marked as safe "
+                                "to insert." % (cls.__name__, idx, field, field))
                     except models.FieldDoesNotExist:
                         # If we can't find a field on the model that matches,
                         # it could be an extra field on the form.
diff --git a/django/core/management/validation.py b/django/core/management/validation.py
index 97164d7..bc0c029 100644
--- a/django/core/management/validation.py
+++ b/django/core/management/validation.py
@@ -120,8 +120,9 @@ def get_validation_errors(outfile, app=None):
 
             if f.rel.through is not None and not isinstance(f.rel.through, basestring):
                 from_model, to_model = cls, f.rel.to
-                if from_model == to_model and f.rel.symmetrical and not f.rel.through._meta.auto_created:
-                    e.add(opts, "Many-to-many fields with intermediate tables cannot be symmetrical.")
+                if from_model == to_model and f.rel.symmetrical and not f.rel.through._meta.insertable_with_only_relationships:
+                    e.add(opts, "Many-to-many fields with 'through' models that are not marked "
+                          "as safe to insert cannot be symmetrical.")
                 seen_from, seen_to, seen_self = False, False, 0
                 for inter_field in f.rel.through._meta.fields:
                     rel_to = getattr(inter_field.rel, 'to', None)
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index fcdda22..e4724d3 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -427,9 +427,9 @@ def create_many_related_manager(superclass, rel=False):
         def get_query_set(self):
             return superclass.get_query_set(self)._next_is_sticky().filter(**(self.core_filters))
 
-        # If the ManyToMany relation has an intermediary model,
-        # the add and remove methods do not exist.
-        if rel.through._meta.auto_created:
+        # Check that the through model can be created without additional fields
+        # before attaching teh add and remove methods
+        if through._meta.insertable_with_only_relationships:
             def add(self, *objs):
                 self._add_items(self.source_field_name, self.target_field_name, *objs)
 
@@ -457,9 +457,9 @@ def create_many_related_manager(superclass, rel=False):
         def create(self, **kwargs):
             # This check needs to be done here, since we can't later remove this
             # from the method lookup table, as we do with add and remove.
-            if not rel.through._meta.auto_created:
-                opts = through._meta
-                raise AttributeError, "Cannot use create() on a ManyToManyField which specifies an intermediary model. Use %s.%s's Manager instead." % (opts.app_label, opts.object_name)
+            opts = through._meta
+            if not opts.insertable_with_only_relationships:
+                raise AttributeError, "Cannot use create() on this ManyToManyField. Use %s.%s's Manager or set insertable_with_only_relationships on its Meta if appropriate." % (opts.app_label, opts.object_name)
             new_obj = super(ManyRelatedManager, self).create(**kwargs)
             self.add(new_obj)
             return new_obj
@@ -569,9 +569,9 @@ class ManyRelatedObjectsDescriptor(object):
         if instance is None:
             raise AttributeError, "Manager must be accessed via instance"
 
-        if not self.related.field.rel.through._meta.auto_created:
-            opts = self.related.field.rel.through._meta
-            raise AttributeError, "Cannot set values on a ManyToManyField which specifies an intermediary model. Use %s.%s's Manager instead." % (opts.app_label, opts.object_name)
+        opts = self.related.field.rel.through._meta
+        if not opts.insertable_with_only_relationships:
+            raise AttributeError, "Cannot set values on this ManyToManyField. Use %s.%s's Manager or set insertable_with_only_relationships on its Meta if appropriate." % (opts.app_label, opts.object_name)
 
         manager = self.__get__(instance)
         manager.clear()
@@ -619,9 +619,9 @@ class ReverseManyRelatedObjectsDescriptor(object):
         if instance is None:
             raise AttributeError, "Manager must be accessed via instance"
 
-        if not self.field.rel.through._meta.auto_created:
-            opts = self.field.rel.through._meta
-            raise AttributeError, "Cannot set values on a ManyToManyField which specifies an intermediary model.  Use %s.%s's Manager instead." % (opts.app_label, opts.object_name)
+        opts = self.field.rel.through._meta
+        if not opts.insertable_with_only_relationships:
+            raise AttributeError, "Cannot set values on this ManyToManyField. Use %s.%s's Manager or set insertable_with_only_relationships on its Meta if appropriate." % (opts.app_label, opts.object_name)
 
         manager = self.__get__(instance)
         manager.clear()
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 05ff54a..f58a8f3 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -18,10 +18,10 @@ from django.utils.datastructures import SortedDict
 # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
 get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).lower().strip()
 
-DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
-                 'unique_together', 'permissions', 'get_latest_by',
-                 'order_with_respect_to', 'app_label', 'db_tablespace',
-                 'abstract', 'managed', 'proxy', 'auto_created')
+DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', 'unique_together',
+                 'permissions', 'get_latest_by', 'order_with_respect_to',
+                 'app_label', 'db_tablespace', 'abstract', 'managed', 'proxy',
+                 'auto_created', 'insertable_with_only_relationships')
 
 class Options(object):
     def __init__(self, meta, app_label=None):
@@ -48,6 +48,7 @@ class Options(object):
         self.parents = SortedDict()
         self.duplicate_targets = {}
         self.auto_created = False
+        self.insertable_with_only_relationships = None
 
         # To handle various inheritance situations, we need to track where
         # managers came from (concrete or abstract base classes).
@@ -104,6 +105,8 @@ class Options(object):
             self.db_table = "%s_%s" % (self.app_label, self.module_name)
             self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
 
+        if self.insertable_with_only_relationships == None:
+            self.insertable_with_only_relationships = self.auto_created
 
     def _prepare(self, model):
         if self.order_with_respect_to:
diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt
index d74f835..8b65989 100644
--- a/docs/ref/models/options.txt
+++ b/docs/ref/models/options.txt
@@ -86,6 +86,18 @@ Example::
 
 See the docs for :meth:`~django.db.models.QuerySet.latest` for more.
 
+``insertable_with_only_relationships``
+-----------------------
+
+.. attribute:: Options.insertable_with_only_relationships
+
+.. versionadded:: 1.2
+
+By default if you specify an explicit model for :attr:`ManyToManyField.through`
+you lose the convience methods of .add and .remove on field as Django can't be
+sure it is safe to create new records with only the two model relationships. If
+you want to retain this behavior you can set this to True.
+
 ``managed``
 -----------------------
 
diff --git a/tests/modeltests/invalid_models/models.py b/tests/modeltests/invalid_models/models.py
index af19963..10657d2 100644
--- a/tests/modeltests/invalid_models/models.py
+++ b/tests/modeltests/invalid_models/models.py
@@ -273,9 +273,9 @@ invalid_models.grouptwo: 'secondary' has a manually-defined m2m relation through
 invalid_models.missingmanualm2mmodel: 'missing_m2m' specifies an m2m relation through model MissingM2MModel, which has not been installed
 invalid_models.group: The model Group has two manually-defined m2m relations through the model Membership, which is not permitted. Please consider using an extra field on your intermediary model instead.
 invalid_models.group: Intermediary model RelationshipDoubleFK has more than one foreign key to Person, which is ambiguous and is not permitted.
-invalid_models.personselfrefm2m: Many-to-many fields with intermediate tables cannot be symmetrical.
+invalid_models.personselfrefm2m: Many-to-many fields with 'through' models that are not marked as safe to insert cannot be symmetrical.
 invalid_models.personselfrefm2m: Intermediary model RelationshipTripleFK has more than two foreign keys to PersonSelfRefM2M, which is ambiguous and is not permitted.
-invalid_models.personselfrefm2mexplicit: Many-to-many fields with intermediate tables cannot be symmetrical.
+invalid_models.personselfrefm2mexplicit: Many-to-many fields with 'through' models that are not marked as safe to insert cannot be symmetrical.
 invalid_models.abstractrelationmodel: 'fk1' has a relation with model AbstractModel, which has either not been installed or is abstract.
 invalid_models.abstractrelationmodel: 'fk2' has an m2m relation with model AbstractModel, which has either not been installed or is abstract.
 invalid_models.uniquem2m: ManyToManyFields cannot be unique.  Remove the unique argument on 'unique_people'.
diff --git a/tests/modeltests/m2m_through/models.py b/tests/modeltests/m2m_through/models.py
index 16f303d..ad35fca 100644
--- a/tests/modeltests/m2m_through/models.py
+++ b/tests/modeltests/m2m_through/models.py
@@ -133,7 +133,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'add'
 >>> rock.members.create(name='Anne')
 Traceback (most recent call last):
 ...
-AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead.
+AttributeError: Cannot use create() on this ManyToManyField. Use m2m_through.Membership's Manager or set insertable_with_only_relationships on its Meta if appropriate.
 
 # Remove has similar complications, and is not provided either.
 >>> rock.members.remove(jim)
@@ -160,7 +160,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'remove'
 >>> rock.members = backup
 Traceback (most recent call last):
 ...
-AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model.  Use m2m_through.Membership's Manager instead.
+AttributeError: Cannot set values on this ManyToManyField. Use m2m_through.Membership's Manager or set insertable_with_only_relationships on its Meta if appropriate.
 
 # Let's re-save those instances that we've cleared.
 >>> m1.save()
@@ -184,7 +184,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'add'
 >>> bob.group_set.create(name='Funk')
 Traceback (most recent call last):
 ...
-AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead.
+AttributeError: Cannot use create() on this ManyToManyField. Use m2m_through.Membership's Manager or set insertable_with_only_relationships on its Meta if appropriate.
 
 # Remove has similar complications, and is not provided either.
 >>> jim.group_set.remove(rock)
@@ -209,7 +209,7 @@ AttributeError: 'ManyRelatedManager' object has no attribute 'remove'
 >>> jim.group_set = backup
 Traceback (most recent call last):
 ...
-AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model.  Use m2m_through.Membership's Manager instead.
+AttributeError: Cannot set values on this ManyToManyField. Use m2m_through.Membership's Manager or set insertable_with_only_relationships on its Meta if appropriate.
 
 # Let's re-save those instances that we've cleared.
 >>> m1.save()
diff --git a/tests/regressiontests/admin_validation/models.py b/tests/regressiontests/admin_validation/models.py
index eb53a9d..a69bb2f 100644
--- a/tests/regressiontests/admin_validation/models.py
+++ b/tests/regressiontests/admin_validation/models.py
@@ -120,7 +120,7 @@ Exception: <class 'regressiontests.admin_validation.models.TwoAlbumFKAndAnE'> ha
 >>> validate(BookAdmin, Book)
 Traceback (most recent call last):
     ...
-ImproperlyConfigured: 'BookAdmin.fields' can't include the ManyToManyField field 'authors' because 'authors' manually specifies a 'through' model.
+ImproperlyConfigured: 'BookAdmin.fields' can't include the ManyToManyField field 'authors' because 'authors' has a 'through' model that is not marked as safe to insert.
 
 >>> class FieldsetBookAdmin(admin.ModelAdmin):
 ...     fieldsets = (
@@ -131,7 +131,7 @@ ImproperlyConfigured: 'BookAdmin.fields' can't include the ManyToManyField field
 >>> validate(FieldsetBookAdmin, Book)
 Traceback (most recent call last):
    ...
-ImproperlyConfigured: 'FieldsetBookAdmin.fieldsets[1][1]['fields']' can't include the ManyToManyField field 'authors' because 'authors' manually specifies a 'through' model.
+ImproperlyConfigured: 'FieldsetBookAdmin.fieldsets[1][1]['fields']' can't include the ManyToManyField field 'authors' because 'authors' has a 'through' model that is not marked as safe to insert.
 
 >>> class NestedFieldsetAdmin(admin.ModelAdmin):
 ...    fieldsets = (
diff --git a/tests/regressiontests/m2m_through_regress/models.py b/tests/regressiontests/m2m_through_regress/models.py
index 56aecd6..9c5b50d 100644
--- a/tests/regressiontests/m2m_through_regress/models.py
+++ b/tests/regressiontests/m2m_through_regress/models.py
@@ -84,22 +84,22 @@ __test__ = {'API_TESTS':"""
 >>> bob.group_set = []
 Traceback (most recent call last):
 ...
-AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model.  Use m2m_through_regress.Membership's Manager instead.
+AttributeError: Cannot set values on this ManyToManyField. Use m2m_through_regress.Membership's Manager or set insertable_with_only_relationships on its Meta if appropriate.
 
 >>> roll.members = []
 Traceback (most recent call last):
 ...
-AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model.  Use m2m_through_regress.Membership's Manager instead.
+AttributeError: Cannot set values on this ManyToManyField. Use m2m_through_regress.Membership's Manager or set insertable_with_only_relationships on its Meta if appropriate.
 
 >>> rock.members.create(name='Anne')
 Traceback (most recent call last):
 ...
-AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model.  Use m2m_through_regress.Membership's Manager instead.
+AttributeError: Cannot use create() on this ManyToManyField. Use m2m_through_regress.Membership's Manager or set insertable_with_only_relationships on its Meta if appropriate.
 
 >>> bob.group_set.create(name='Funk')
 Traceback (most recent call last):
 ...
-AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model.  Use m2m_through_regress.Membership's Manager instead.
+AttributeError: Cannot use create() on this ManyToManyField. Use m2m_through_regress.Membership's Manager or set insertable_with_only_relationships on its Meta if appropriate.
 
 # Now test that the intermediate with a relationship outside
 # the current app (i.e., UserMembership) workds
