Fixed #24163 -- Removed unique constraint after index on MySQL
Thanks Łukasz Harasimowicz for the report.
This commit is contained in:
parent
8e435a5640
commit
5792e6a88c
|
@ -488,18 +488,6 @@ class BaseDatabaseSchemaEditor(object):
|
|||
old_db_params, new_db_params, strict=False):
|
||||
"""Actually perform a "physical" (non-ManyToMany) field update."""
|
||||
|
||||
# Has unique been removed?
|
||||
if old_field.unique and (not new_field.unique or (not old_field.primary_key and new_field.primary_key)):
|
||||
# Find the unique constraint for this field
|
||||
constraint_names = self._constraint_names(model, [old_field.column], unique=True)
|
||||
if strict and len(constraint_names) != 1:
|
||||
raise ValueError("Found wrong number (%s) of unique constraints for %s.%s" % (
|
||||
len(constraint_names),
|
||||
model._meta.db_table,
|
||||
old_field.column,
|
||||
))
|
||||
for constraint_name in constraint_names:
|
||||
self.execute(self._delete_constraint_sql(self.sql_delete_unique, model, constraint_name))
|
||||
# Drop any FK constraints, we'll remake them later
|
||||
fks_dropped = set()
|
||||
if old_field.rel and old_field.db_constraint:
|
||||
|
@ -513,6 +501,18 @@ class BaseDatabaseSchemaEditor(object):
|
|||
for fk_name in fk_names:
|
||||
fks_dropped.add((old_field.column,))
|
||||
self.execute(self._delete_constraint_sql(self.sql_delete_fk, model, fk_name))
|
||||
# Has unique been removed?
|
||||
if old_field.unique and (not new_field.unique or (not old_field.primary_key and new_field.primary_key)):
|
||||
# Find the unique constraint for this field
|
||||
constraint_names = self._constraint_names(model, [old_field.column], unique=True)
|
||||
if strict and len(constraint_names) != 1:
|
||||
raise ValueError("Found wrong number (%s) of unique constraints for %s.%s" % (
|
||||
len(constraint_names),
|
||||
model._meta.db_table,
|
||||
old_field.column,
|
||||
))
|
||||
for constraint_name in constraint_names:
|
||||
self.execute(self._delete_constraint_sql(self.sql_delete_unique, model, constraint_name))
|
||||
# Drop incoming FK constraints if we're a primary key and things are going
|
||||
# to change.
|
||||
if old_field.primary_key and new_field.primary_key and old_type != new_type:
|
||||
|
|
|
@ -14,3 +14,6 @@ Bugfixes
|
|||
|
||||
* Made the migration's ``RenameModel`` operation rename ``ManyToManyField``
|
||||
tables (:ticket:`24135`).
|
||||
|
||||
* Fixed a migration crash on MySQL when migrating from a ``OneToOneField`` to a
|
||||
``ForeignKey`` (:ticket:`24163`).
|
||||
|
|
|
@ -67,6 +67,16 @@ class BookWeak(models.Model):
|
|||
apps = new_apps
|
||||
|
||||
|
||||
class BookWithO2O(models.Model):
|
||||
author = models.OneToOneField(Author)
|
||||
title = models.CharField(max_length=100, db_index=True)
|
||||
pub_date = models.DateTimeField()
|
||||
|
||||
class Meta:
|
||||
apps = new_apps
|
||||
db_table = "schema_book"
|
||||
|
||||
|
||||
class BookWithM2M(models.Model):
|
||||
author = models.ForeignKey(Author)
|
||||
title = models.CharField(max_length=100, db_index=True)
|
||||
|
|
|
@ -5,12 +5,12 @@ from django.test import TransactionTestCase
|
|||
from django.db import connection, DatabaseError, IntegrityError, OperationalError
|
||||
from django.db.models.fields import (BinaryField, BooleanField, CharField, IntegerField,
|
||||
PositiveIntegerField, SlugField, TextField)
|
||||
from django.db.models.fields.related import ManyToManyField, ForeignKey
|
||||
from django.db.models.fields.related import ForeignKey, ManyToManyField, OneToOneField
|
||||
from django.db.transaction import atomic
|
||||
from .models import (Author, AuthorWithDefaultHeight, AuthorWithM2M, Book, BookWithLongName,
|
||||
BookWithSlug, BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename,
|
||||
UniqueTest, Thing, TagThrough, BookWithM2MThrough, AuthorTag, AuthorWithM2MThrough,
|
||||
AuthorWithEvenLongerName, BookWeak, Note)
|
||||
AuthorWithEvenLongerName, BookWeak, Note, BookWithO2O)
|
||||
|
||||
|
||||
class SchemaTests(TransactionTestCase):
|
||||
|
@ -28,7 +28,7 @@ class SchemaTests(TransactionTestCase):
|
|||
Author, AuthorWithM2M, Book, BookWithLongName, BookWithSlug,
|
||||
BookWithM2M, Tag, TagIndexed, TagM2MTest, TagUniqueRename, UniqueTest,
|
||||
Thing, TagThrough, BookWithM2MThrough, AuthorWithEvenLongerName,
|
||||
BookWeak,
|
||||
BookWeak, BookWithO2O,
|
||||
]
|
||||
|
||||
# Utility functions
|
||||
|
@ -528,6 +528,106 @@ class SchemaTests(TransactionTestCase):
|
|||
else:
|
||||
self.fail("No FK constraint for author_id found")
|
||||
|
||||
@unittest.skipUnless(connection.features.supports_foreign_keys, "No FK support")
|
||||
def test_alter_o2o_to_fk(self):
|
||||
"""
|
||||
#24163 - Tests altering of OneToOne to FK
|
||||
"""
|
||||
# Create the table
|
||||
with connection.schema_editor() as editor:
|
||||
editor.create_model(Author)
|
||||
editor.create_model(BookWithO2O)
|
||||
# Ensure the field is right to begin with
|
||||
columns = self.column_classes(BookWithO2O)
|
||||
self.assertEqual(columns['author_id'][0], "IntegerField")
|
||||
# Make sure the FK and unique constraints are present
|
||||
constraints = self.get_constraints(BookWithO2O._meta.db_table)
|
||||
author_is_fk = False
|
||||
author_is_unique = False
|
||||
for name, details in constraints.items():
|
||||
if details['columns'] == ['author_id']:
|
||||
if details['foreign_key'] and details['foreign_key'] == ('schema_author', 'id'):
|
||||
author_is_fk = True
|
||||
if details['unique']:
|
||||
author_is_unique = True
|
||||
self.assertTrue(author_is_fk, "No FK constraint for author_id found")
|
||||
self.assertTrue(author_is_unique, "No unique constraint for author_id found")
|
||||
# Alter the O2O to FK
|
||||
new_field = ForeignKey(Author)
|
||||
new_field.set_attributes_from_name("author")
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_field(
|
||||
BookWithO2O,
|
||||
BookWithO2O._meta.get_field("author"),
|
||||
new_field,
|
||||
strict=True,
|
||||
)
|
||||
# Ensure the field is right afterwards
|
||||
columns = self.column_classes(Book)
|
||||
self.assertEqual(columns['author_id'][0], "IntegerField")
|
||||
# Make sure the FK constraint is present and unique constraint is absent
|
||||
constraints = self.get_constraints(Book._meta.db_table)
|
||||
author_is_fk = False
|
||||
author_is_unique = True
|
||||
for name, details in constraints.items():
|
||||
if details['columns'] == ['author_id']:
|
||||
if details['foreign_key'] and details['foreign_key'] == ('schema_author', 'id'):
|
||||
author_is_fk = True
|
||||
if not details['unique']:
|
||||
author_is_unique = False
|
||||
self.assertTrue(author_is_fk, "No FK constraint for author_id found")
|
||||
self.assertFalse(author_is_unique, "Unique constraint for author_id found")
|
||||
|
||||
@unittest.skipUnless(connection.features.supports_foreign_keys, "No FK support")
|
||||
def test_alter_fk_to_o2o(self):
|
||||
"""
|
||||
#24163 - Tests altering of FK to OneToOne
|
||||
"""
|
||||
# Create the table
|
||||
with connection.schema_editor() as editor:
|
||||
editor.create_model(Author)
|
||||
editor.create_model(Book)
|
||||
# Ensure the field is right to begin with
|
||||
columns = self.column_classes(Book)
|
||||
self.assertEqual(columns['author_id'][0], "IntegerField")
|
||||
# Make sure the FK constraint is present and unique constraint is absent
|
||||
constraints = self.get_constraints(Book._meta.db_table)
|
||||
author_is_fk = False
|
||||
author_is_unique = True
|
||||
for name, details in constraints.items():
|
||||
if details['columns'] == ['author_id']:
|
||||
if details['foreign_key'] and details['foreign_key'] == ('schema_author', 'id'):
|
||||
author_is_fk = True
|
||||
if not details['unique']:
|
||||
author_is_unique = False
|
||||
self.assertTrue(author_is_fk, "No FK constraint for author_id found")
|
||||
self.assertFalse(author_is_unique, "Unique constraint for author_id found")
|
||||
# Alter the O2O to FK
|
||||
new_field = OneToOneField(Author)
|
||||
new_field.set_attributes_from_name("author")
|
||||
with connection.schema_editor() as editor:
|
||||
editor.alter_field(
|
||||
Book,
|
||||
Book._meta.get_field("author"),
|
||||
new_field,
|
||||
strict=True,
|
||||
)
|
||||
# Ensure the field is right afterwards
|
||||
columns = self.column_classes(BookWithO2O)
|
||||
self.assertEqual(columns['author_id'][0], "IntegerField")
|
||||
# Make sure the FK and unique constraints are present
|
||||
constraints = self.get_constraints(BookWithO2O._meta.db_table)
|
||||
author_is_fk = False
|
||||
author_is_unique = False
|
||||
for name, details in constraints.items():
|
||||
if details['columns'] == ['author_id']:
|
||||
if details['foreign_key'] and details['foreign_key'] == ('schema_author', 'id'):
|
||||
author_is_fk = True
|
||||
if details['unique']:
|
||||
author_is_unique = True
|
||||
self.assertTrue(author_is_fk, "No FK constraint for author_id found")
|
||||
self.assertTrue(author_is_unique, "No unique constraint for author_id found")
|
||||
|
||||
def test_alter_implicit_id_to_explicit(self):
|
||||
"""
|
||||
Should be able to convert an implicit "id" field to an explicit "id"
|
||||
|
|
Loading…
Reference in New Issue