Fixed #22183: Through M2Ms now correctly handled

This commit is contained in:
Andrew Godwin 2014-03-08 15:57:25 -08:00
parent cdf6eba181
commit 6b07804474
5 changed files with 44 additions and 3 deletions

View File

@ -262,7 +262,8 @@ class BaseDatabaseSchemaEditor(object):
}) })
# Make M2M tables # Make M2M tables
for field in model._meta.local_many_to_many: for field in model._meta.local_many_to_many:
self.create_model(field.rel.through) if field.rel.through._meta.auto_created:
self.create_model(field.rel.through)
def delete_model(self, model): def delete_model(self, model):
""" """

View File

@ -2032,6 +2032,11 @@ class ManyToManyField(RelatedField):
kwargs['to'] = self.rel.to kwargs['to'] = self.rel.to
else: else:
kwargs['to'] = "%s.%s" % (self.rel.to._meta.app_label, self.rel.to._meta.object_name) 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 # If swappable is True, then see if we're actually pointing to the target
# of a swap. # of a swap.
swappable_setting = self.swappable_setting swappable_setting = self.swappable_setting

View File

@ -242,6 +242,12 @@ class FieldDeconstructionTests(TestCase):
self.assertEqual(args, []) self.assertEqual(args, [])
self.assertEqual(kwargs, {"to": "auth.User"}) self.assertEqual(kwargs, {"to": "auth.User"})
self.assertEqual(kwargs['to'].setting_name, "AUTH_USER_MODEL") 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") @override_settings(AUTH_USER_MODEL="auth.Permission")
def test_many_to_many_field_swapped(self): def test_many_to_many_field_swapped(self):

View File

@ -44,6 +44,21 @@ class BookWithM2M(models.Model):
apps = new_apps 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): class BookWithSlug(models.Model):
author = models.ForeignKey(Author) author = models.ForeignKey(Author)
title = models.CharField(max_length=100, db_index=True) title = models.CharField(max_length=100, db_index=True)

View File

@ -9,7 +9,7 @@ from django.db.models.fields.related import ManyToManyField, ForeignKey
from django.db.transaction import atomic from django.db.transaction import atomic
from .models import (Author, AuthorWithM2M, Book, BookWithLongName, from .models import (Author, AuthorWithM2M, Book, BookWithLongName,
BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename,
UniqueTest, Thing) UniqueTest, Thing, TagThrough, BookWithM2MThrough)
class SchemaTests(TransactionTestCase): class SchemaTests(TransactionTestCase):
@ -26,7 +26,7 @@ class SchemaTests(TransactionTestCase):
models = [ models = [
Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug, Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug,
BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest,
Thing Thing, TagThrough, BookWithM2MThrough
] ]
# Utility functions # Utility functions
@ -310,6 +310,20 @@ class SchemaTests(TransactionTestCase):
columns = self.column_classes(BookWithM2M._meta.get_field_by_name("tags")[0].rel.through) columns = self.column_classes(BookWithM2M._meta.get_field_by_name("tags")[0].rel.through)
self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField") 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): def test_m2m(self):
""" """
Tests adding/removing M2M fields on models Tests adding/removing M2M fields on models