Opened 16 months ago

Last modified 16 months ago

#34647 closed Bug

Foreign Key index names are not renamed when a model is renamed — at Initial Version

Reported by: Steven Mapes Owned by: nobody
Component: Migrations Version: 4.2
Severity: Normal Keywords: migrations, sql, foreign_key, mysql
Cc: Bhuvnesh, Simon Charette, Mariusz Felisiak, David, Wobrock Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I encountered an issue yesterday where Django created a foreign key that already exists.

The way I encountered this was to take a model called CriticalPathTemplate with a property called archived_by which is a foreign key to the user table, then renamed the model to CriticalPathTemplateOld and ran migrations. This renamed the table but left the indexes as they were.

I then added a new model called CriticalPathTemplate (business requirement) and ran makemigrations and migrate. The migrations were made but makemigrations failed due to a foreign key name clash. I ended up renaming the column but then hit further issues for the next FK in the table E.G {{{django.db.utils.OperationalError: (1826, "Duplicate foreign key constraint name 'critical_path_templa_origin_agent_shortco_f9715e87_fk_address_s'")
}}}

Looking at the output of sqlmigrate for the new migration and comparing the name of the foreign keys that Django generated I can see several that are going to clash as the indexes on the old table were not renamed when the table was renamed by the migration.

I did manage to re-create the issue this morning on both SQL Lite and MySQL backends on a new project, but having sat down to create the bug report I am now struggling to have Django invoking migrations.RenameModel as it's just dropping and then creating the models, with all data, instead.

I will have another go at the test project but it simply had two models

from django.db import models
from django.contrib.auth.models import User

class ThisIsAModelWithALongName(models.Model):
    """The Original Model"""
    name = models.CharField(max_length=128, blank=True)
    archived_by = models.ForeignKey(User, null=True, blank=True, default=None, related_name="model_with_log_names", on_delete=models.SET_NULL)


class AnotherClassHere(models.Model):
    long_name_property = models.ForeignKey(ThisIsAModelWithALongName, null=True, blank=True, default=None, related_name="another_class_here", on_delete=models.SET_NULL)

I then renamed the model

from django.db import models
from django.contrib.auth.models import User

class ThisIsAModelWithALongNameOld(models.Model):
    """The Original Model"""
    name = models.CharField(max_length=128, blank=True)
    archived_by = models.ForeignKey(User, null=True, blank=True, default=None, related_name="model_with_log_names_old", on_delete=models.SET_NULL)


class AnotherClassHere(models.Model):
    long_name_property_old = models.ForeignKey(ThisIsAModelWithALongNameOld, null=True, blank=True, default=None, related_name="another_class_here", on_delete=models.SET_NULL)

Then I created the new model with the same name as the old model

from django.db import models
from django.contrib.auth.models import User

class ThisIsAModelWithALongNameOld(models.Model):
    """The Original Model"""
    name = models.CharField(max_length=128, blank=True)
    archived_by = models.ForeignKey(User, null=True, blank=True, default=None, related_name="model_with_log_names_old", on_delete=models.SET_NULL)


class ThisIsAModelWithALongName(models.Model):
    """The NEW Model"""
    name = models.CharField(max_length=128, blank=True)
    archived_by = models.ForeignKey(User, null=True, blank=True, default=None, related_name="model_with_log_names", on_delete=models.SET_NULL)


class AnotherClassHere(models.Model):
    long_name_property_old = models.ForeignKey(ThisIsAModelWithALongNameOld, null=True, blank=True, default=None, related_name="another_class_here", on_delete=models.SET_NULL)
    long_name_property = models.ForeignKey(ThisIsAModelWithALongName, null=True, blank=True, default=None, related_name="another_class_here", on_delete=models.SET_NULL)

MySQL version was 8.0.33 using mysqlclient==2.1.1

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top