Loading django/db/backends/schema.py +2 −1 Original line number Diff line number Diff line Loading @@ -262,6 +262,7 @@ class BaseDatabaseSchemaEditor(object): }) # Make M2M tables for field in model._meta.local_many_to_many: if field.rel.through._meta.auto_created: self.create_model(field.rel.through) def delete_model(self, model): Loading django/db/models/fields/related.py +5 −0 Original line number Diff line number Diff line Loading @@ -2032,6 +2032,11 @@ class ManyToManyField(RelatedField): kwargs['to'] = self.rel.to else: kwargs['to'] = "%s.%s" % (self.rel.to._meta.app_label, self.rel.to._meta.object_name) if getattr(self.rel, 'through', None) is not None: if isinstance(self.rel.through, six.string_types): kwargs['through'] = self.rel.through else: kwargs['through'] = "%s.%s" % (self.rel.through._meta.app_label, self.rel.through._meta.object_name) # If swappable is True, then see if we're actually pointing to the target # of a swap. swappable_setting = self.swappable_setting Loading tests/field_deconstruction/tests.py +6 −0 Original line number Diff line number Diff line Loading @@ -242,6 +242,12 @@ class FieldDeconstructionTests(TestCase): self.assertEqual(args, []) self.assertEqual(kwargs, {"to": "auth.User"}) self.assertEqual(kwargs['to'].setting_name, "AUTH_USER_MODEL") # Test through field = models.ManyToManyField("auth.Permission", through="auth.Group") name, path, args, kwargs = field.deconstruct() self.assertEqual(path, "django.db.models.ManyToManyField") self.assertEqual(args, []) self.assertEqual(kwargs, {"to": "auth.Permission", "through": "auth.Group"}) @override_settings(AUTH_USER_MODEL="auth.Permission") def test_many_to_many_field_swapped(self): Loading tests/schema/models.py +15 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,21 @@ class BookWithM2M(models.Model): apps = new_apps class TagThrough(models.Model): book = models.ForeignKey("schema.BookWithM2MThrough") tag = models.ForeignKey("schema.TagM2MTest") class Meta: apps = new_apps class BookWithM2MThrough(models.Model): tags = models.ManyToManyField("TagM2MTest", related_name="books", through=TagThrough) class Meta: apps = new_apps class BookWithSlug(models.Model): author = models.ForeignKey(Author) title = models.CharField(max_length=100, db_index=True) Loading tests/schema/tests.py +16 −2 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ from django.db.models.fields.related import ManyToManyField, ForeignKey from django.db.transaction import atomic from .models import (Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest, Thing) UniqueTest, Thing, TagThrough, BookWithM2MThrough) class SchemaTests(TransactionTestCase): Loading @@ -26,7 +26,7 @@ class SchemaTests(TransactionTestCase): models = [ Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest, Thing Thing, TagThrough, BookWithM2MThrough ] # Utility functions Loading Loading @@ -310,6 +310,20 @@ class SchemaTests(TransactionTestCase): columns = self.column_classes(BookWithM2M._meta.get_field_by_name("tags")[0].rel.through) self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField") def test_m2m_create_through(self): """ Tests M2M fields on models during creation with through models """ # Create the tables with connection.schema_editor() as editor: editor.create_model(TagThrough) editor.create_model(TagM2MTest) editor.create_model(BookWithM2MThrough) # Ensure there is now an m2m table there columns = self.column_classes(TagThrough) self.assertEqual(columns['book_id'][0], "IntegerField") self.assertEqual(columns['tag_id'][0], "IntegerField") def test_m2m(self): """ Tests adding/removing M2M fields on models Loading Loading
django/db/backends/schema.py +2 −1 Original line number Diff line number Diff line Loading @@ -262,6 +262,7 @@ class BaseDatabaseSchemaEditor(object): }) # Make M2M tables for field in model._meta.local_many_to_many: if field.rel.through._meta.auto_created: self.create_model(field.rel.through) def delete_model(self, model): Loading
django/db/models/fields/related.py +5 −0 Original line number Diff line number Diff line Loading @@ -2032,6 +2032,11 @@ class ManyToManyField(RelatedField): kwargs['to'] = self.rel.to else: kwargs['to'] = "%s.%s" % (self.rel.to._meta.app_label, self.rel.to._meta.object_name) if getattr(self.rel, 'through', None) is not None: if isinstance(self.rel.through, six.string_types): kwargs['through'] = self.rel.through else: kwargs['through'] = "%s.%s" % (self.rel.through._meta.app_label, self.rel.through._meta.object_name) # If swappable is True, then see if we're actually pointing to the target # of a swap. swappable_setting = self.swappable_setting Loading
tests/field_deconstruction/tests.py +6 −0 Original line number Diff line number Diff line Loading @@ -242,6 +242,12 @@ class FieldDeconstructionTests(TestCase): self.assertEqual(args, []) self.assertEqual(kwargs, {"to": "auth.User"}) self.assertEqual(kwargs['to'].setting_name, "AUTH_USER_MODEL") # Test through field = models.ManyToManyField("auth.Permission", through="auth.Group") name, path, args, kwargs = field.deconstruct() self.assertEqual(path, "django.db.models.ManyToManyField") self.assertEqual(args, []) self.assertEqual(kwargs, {"to": "auth.Permission", "through": "auth.Group"}) @override_settings(AUTH_USER_MODEL="auth.Permission") def test_many_to_many_field_swapped(self): Loading
tests/schema/models.py +15 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,21 @@ class BookWithM2M(models.Model): apps = new_apps class TagThrough(models.Model): book = models.ForeignKey("schema.BookWithM2MThrough") tag = models.ForeignKey("schema.TagM2MTest") class Meta: apps = new_apps class BookWithM2MThrough(models.Model): tags = models.ManyToManyField("TagM2MTest", related_name="books", through=TagThrough) class Meta: apps = new_apps class BookWithSlug(models.Model): author = models.ForeignKey(Author) title = models.CharField(max_length=100, db_index=True) Loading
tests/schema/tests.py +16 −2 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ from django.db.models.fields.related import ManyToManyField, ForeignKey from django.db.transaction import atomic from .models import (Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest, Thing) UniqueTest, Thing, TagThrough, BookWithM2MThrough) class SchemaTests(TransactionTestCase): Loading @@ -26,7 +26,7 @@ class SchemaTests(TransactionTestCase): models = [ Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest, Thing Thing, TagThrough, BookWithM2MThrough ] # Utility functions Loading Loading @@ -310,6 +310,20 @@ class SchemaTests(TransactionTestCase): columns = self.column_classes(BookWithM2M._meta.get_field_by_name("tags")[0].rel.through) self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField") def test_m2m_create_through(self): """ Tests M2M fields on models during creation with through models """ # Create the tables with connection.schema_editor() as editor: editor.create_model(TagThrough) editor.create_model(TagM2MTest) editor.create_model(BookWithM2MThrough) # Ensure there is now an m2m table there columns = self.column_classes(TagThrough) self.assertEqual(columns['book_id'][0], "IntegerField") self.assertEqual(columns['tag_id'][0], "IntegerField") def test_m2m(self): """ Tests adding/removing M2M fields on models Loading