From 52edc16086e3c28a78c31975bb4da2f9450590b4 Mon Sep 17 00:00:00 2001
From: Andrew Godwin <andrew@aeracode.org>
Date: Mon, 19 Aug 2013 13:50:26 +0100
Subject: [PATCH] Add more stringent M2M tests and fix the bug they exposed

---
 django/db/models/options.py         |  6 +++---
 tests/migrations/test_operations.py |  9 ++++++++-
 tests/schema/models.py              | 10 +++++++++-
 tests/schema/tests.py               | 24 ++++++++++++------------
 4 files changed, 32 insertions(+), 17 deletions(-)

diff --git a/django/db/models/options.py b/django/db/models/options.py
index d39873fd70..14f73c301f 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -9,7 +9,7 @@ from django.conf import settings
 from django.db.models.fields.related import ManyToManyRel
 from django.db.models.fields import AutoField, FieldDoesNotExist
 from django.db.models.fields.proxy import OrderWrt
-from django.db.models.loading import get_models, app_cache_ready, cache
+from django.db.models.loading import app_cache_ready, cache
 from django.utils import six
 from django.utils.functional import cached_property
 from django.utils.encoding import force_text, smart_text, python_2_unicode_compatible
@@ -495,7 +495,7 @@ class Options(object):
                     cache[obj] = model
         # Collect also objects which are in relation to some proxy child/parent of self.
         proxy_cache = cache.copy()
-        for klass in get_models(include_auto_created=True, only_installed=False):
+        for klass in self.app_cache.get_models(include_auto_created=True, only_installed=False):
             if not klass._meta.swapped:
                 for f in klass._meta.local_fields:
                     if f.rel and not isinstance(f.rel.to, six.string_types) and f.generate_reverse_relation:
@@ -538,7 +538,7 @@ class Options(object):
                     cache[obj] = parent
                 else:
                     cache[obj] = model
-        for klass in get_models(only_installed=False):
+        for klass in self.app_cache.get_models(only_installed=False):
             if not klass._meta.swapped:
                 for f in klass._meta.local_many_to_many:
                     if (f.rel
diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py
index 33b870a335..2ff3f73b8a 100644
--- a/tests/migrations/test_operations.py
+++ b/tests/migrations/test_operations.py
@@ -116,7 +116,7 @@ class OperationTests(MigrationTestBase):
         """
         project_state = self.set_up_test_model("test_adflmm", second_model=True)
         # Test the state alteration
-        operation = migrations.AddField("Pony", "stables", models.ManyToManyField("Stable"))
+        operation = migrations.AddField("Pony", "stables", models.ManyToManyField("Stable", related_name="ponies"))
         new_state = project_state.clone()
         operation.state_forwards("test_adflmm", new_state)
         self.assertEqual(len(new_state.models["test_adflmm", "pony"].fields), 4)
@@ -126,6 +126,13 @@ class OperationTests(MigrationTestBase):
             operation.database_forwards("test_adflmm", editor, project_state, new_state)
         self.assertTableExists("test_adflmm_pony_stables")
         self.assertColumnNotExists("test_adflmm_pony", "stables")
+        # Make sure the M2M field actually works
+        app_cache = new_state.render()
+        Pony = app_cache.get_model("test_adflmm", "Pony")
+        p = Pony.objects.create(pink=False, weight=4.55)
+        p.stables.create()
+        self.assertEqual(p.stables.count(), 1)
+        p.stables.all().delete()
         # And test reversal
         with connection.schema_editor() as editor:
             operation.database_backwards("test_adflmm", editor, new_state, project_state)
diff --git a/tests/schema/models.py b/tests/schema/models.py
index 69cf06f3c4..dc717ec105 100644
--- a/tests/schema/models.py
+++ b/tests/schema/models.py
@@ -37,7 +37,7 @@ class BookWithM2M(models.Model):
     author = models.ForeignKey(Author)
     title = models.CharField(max_length=100, db_index=True)
     pub_date = models.DateTimeField()
-    tags = models.ManyToManyField("Tag", related_name="books")
+    tags = models.ManyToManyField("TagM2MTest", related_name="books")
 
     class Meta:
         app_cache = new_app_cache
@@ -62,6 +62,14 @@ class Tag(models.Model):
         app_cache = new_app_cache
 
 
+class TagM2MTest(models.Model):
+    title = models.CharField(max_length=255)
+    slug = models.SlugField(unique=True)
+
+    class Meta:
+        app_cache = new_app_cache
+
+
 class TagIndexed(models.Model):
     title = models.CharField(max_length=255)
     slug = models.SlugField(unique=True)
diff --git a/tests/schema/tests.py b/tests/schema/tests.py
index d4e76e8567..bf9fa6bbcc 100644
--- a/tests/schema/tests.py
+++ b/tests/schema/tests.py
@@ -6,7 +6,7 @@ from django.db import connection, DatabaseError, IntegrityError
 from django.db.models.fields import IntegerField, TextField, CharField, SlugField
 from django.db.models.fields.related import ManyToManyField, ForeignKey
 from django.db.transaction import atomic
-from .models import Author, AuthorWithM2M, Book, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagUniqueRename, UniqueTest
+from .models import Author, AuthorWithM2M, Book, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest
 
 
 class SchemaTests(TransactionTestCase):
@@ -20,7 +20,7 @@ class SchemaTests(TransactionTestCase):
     
     available_apps = []
 
-    models = [Author, AuthorWithM2M, Book, BookWithSlug, BookWithM2M, Tag, TagUniqueRename, UniqueTest]
+    models = [Author, AuthorWithM2M, Book, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest]
     no_table_strings = ["no such table", "unknown table", "does not exist"]
 
     # Utility functions
@@ -234,7 +234,7 @@ class SchemaTests(TransactionTestCase):
             editor.create_model(BookWithM2M)
         # Ensure there is now an m2m table there
         columns = self.column_classes(BookWithM2M._meta.get_field_by_name("tags")[0].rel.through)
-        self.assertEqual(columns['tag_id'][0], "IntegerField")
+        self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField")
 
     def test_m2m(self):
         """
@@ -243,9 +243,9 @@ class SchemaTests(TransactionTestCase):
         # Create the tables
         with connection.schema_editor() as editor:
             editor.create_model(AuthorWithM2M)
-            editor.create_model(Tag)
+            editor.create_model(TagM2MTest)
         # Create an M2M field
-        new_field = ManyToManyField("schema.Tag", related_name="authors")
+        new_field = ManyToManyField("schema.TagM2MTest", related_name="authors")
         new_field.contribute_to_class(AuthorWithM2M, "tags")
         try:
             # Ensure there's no m2m table there
@@ -258,7 +258,7 @@ class SchemaTests(TransactionTestCase):
                 )
             # Ensure there is now an m2m table there
             columns = self.column_classes(new_field.rel.through)
-            self.assertEqual(columns['tag_id'][0], "IntegerField")
+            self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField")
             # Remove the M2M table again
             with connection.schema_editor() as editor:
                 editor.remove_field(
@@ -279,17 +279,17 @@ class SchemaTests(TransactionTestCase):
         with connection.schema_editor() as editor:
             editor.create_model(Author)
             editor.create_model(BookWithM2M)
-            editor.create_model(Tag)
+            editor.create_model(TagM2MTest)
             editor.create_model(UniqueTest)
-        # Ensure the M2M exists and points to Tag
+        # Ensure the M2M exists and points to TagM2MTest
         constraints = connection.introspection.get_constraints(connection.cursor(), BookWithM2M._meta.get_field_by_name("tags")[0].rel.through._meta.db_table)
         if connection.features.supports_foreign_keys:
             for name, details in constraints.items():
-                if details['columns'] == ["tag_id"] and details['foreign_key']:
-                    self.assertEqual(details['foreign_key'], ('schema_tag', 'id'))
+                if details['columns'] == ["tagm2mtest_id"] and details['foreign_key']:
+                    self.assertEqual(details['foreign_key'], ('schema_tagm2mtest', 'id'))
                     break
             else:
-                self.fail("No FK constraint for tag_id found")
+                self.fail("No FK constraint for tagm2mtest_id found")
         # Repoint the M2M
         new_field = ManyToManyField(UniqueTest)
         new_field.contribute_to_class(BookWithM2M, "uniques")
@@ -310,7 +310,7 @@ class SchemaTests(TransactionTestCase):
                         self.assertEqual(details['foreign_key'], ('schema_uniquetest', 'id'))
                         break
                 else:
-                    self.fail("No FK constraint for tag_id found")
+                    self.fail("No FK constraint for uniquetest_id found")
         finally:
             # Cleanup model states
             BookWithM2M._meta.local_many_to_many.remove(new_field)