[1.7.x] Fixed #22476: Couldn't alter attributes on M2Ms with through= set

This commit is contained in:
Andrew Godwin 2014-05-08 10:33:59 -07:00
parent bc82c0dbac
commit f2bf59a5bc
4 changed files with 49 additions and 3 deletions

View File

@ -483,8 +483,11 @@ class BaseDatabaseSchemaEditor(object):
new_type = new_db_params['type'] new_type = new_db_params['type']
if old_type is None and new_type is None and (old_field.rel.through and new_field.rel.through and old_field.rel.through._meta.auto_created and new_field.rel.through._meta.auto_created): if old_type is None and new_type is None and (old_field.rel.through and new_field.rel.through and old_field.rel.through._meta.auto_created and new_field.rel.through._meta.auto_created):
return self._alter_many_to_many(model, old_field, new_field, strict) return self._alter_many_to_many(model, old_field, new_field, strict)
elif old_type is None and new_type is None and (old_field.rel.through and new_field.rel.through and not old_field.rel.through._meta.auto_created and not new_field.rel.through._meta.auto_created):
# Both sides have through models; this is a no-op.
return
elif old_type is None or new_type is None: elif old_type is None or new_type is None:
raise ValueError("Cannot alter field %s into %s - they are not compatible types (probably means only one is an M2M with implicit through model)" % ( raise ValueError("Cannot alter field %s into %s - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)" % (
old_field, old_field,
new_field, new_field,
)) ))

View File

@ -148,8 +148,11 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
new_type = new_db_params['type'] new_type = new_db_params['type']
if old_type is None and new_type is None and (old_field.rel.through and new_field.rel.through and old_field.rel.through._meta.auto_created and new_field.rel.through._meta.auto_created): if old_type is None and new_type is None and (old_field.rel.through and new_field.rel.through and old_field.rel.through._meta.auto_created and new_field.rel.through._meta.auto_created):
return self._alter_many_to_many(model, old_field, new_field, strict) return self._alter_many_to_many(model, old_field, new_field, strict)
elif old_type is None and new_type is None and (old_field.rel.through and new_field.rel.through and not old_field.rel.through._meta.auto_created and not new_field.rel.through._meta.auto_created):
# Both sides have through models; this is a no-op.
return
elif old_type is None or new_type is None: elif old_type is None or new_type is None:
raise ValueError("Cannot alter field %s into %s - they are not compatible types (probably means only one is an M2M with implicit through model)" % ( raise ValueError("Cannot alter field %s into %s - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)" % (
old_field, old_field,
new_field, new_field,
)) ))

View File

@ -24,6 +24,22 @@ class AuthorWithM2M(models.Model):
apps = new_apps apps = new_apps
class AuthorWithM2MThrough(models.Model):
name = models.CharField(max_length=255)
tags = models.ManyToManyField("schema.TagM2MTest", related_name="authors", through="AuthorTag")
class Meta:
apps = new_apps
class AuthorTag(models.Model):
author = models.ForeignKey("schema.AuthorWithM2MThrough")
tag = models.ForeignKey("schema.TagM2MTest")
class Meta:
apps = new_apps
class Book(models.Model): class Book(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, TagThrough, BookWithM2MThrough) UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorTag, AuthorWithM2MThrough)
class SchemaTests(TransactionTestCase): class SchemaTests(TransactionTestCase):
@ -402,6 +402,30 @@ class SchemaTests(TransactionTestCase):
# Cleanup model states # Cleanup model states
AuthorWithM2M._meta.local_many_to_many.remove(new_field) AuthorWithM2M._meta.local_many_to_many.remove(new_field)
def test_m2m_through_alter(self):
"""
Tests altering M2Ms with explicit through models (should no-op)
"""
# Create the tables
with connection.schema_editor() as editor:
editor.create_model(AuthorTag)
editor.create_model(AuthorWithM2MThrough)
editor.create_model(TagM2MTest)
# Ensure the m2m table is there
self.assertEqual(len(self.column_classes(AuthorTag)), 3)
# "Alter" the field's blankness. This should not actually do anything.
with connection.schema_editor() as editor:
old_field = AuthorWithM2MThrough._meta.get_field_by_name("tags")[0]
new_field = ManyToManyField("schema.TagM2MTest", related_name="authors", through="AuthorTag")
new_field.contribute_to_class(AuthorWithM2MThrough, "tags")
editor.alter_field(
Author,
old_field,
new_field,
)
# Ensure the m2m table is still there
self.assertEqual(len(self.column_classes(AuthorTag)), 3)
def test_m2m_repoint(self): def test_m2m_repoint(self):
""" """
Tests repointing M2M fields Tests repointing M2M fields