Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save x-N0/3e65c945cce8f9f918c066694ef967f5 to your computer and use it in GitHub Desktop.
Save x-N0/3e65c945cce8f9f918c066694ef967f5 to your computer and use it in GitHub Desktop.
Fix: Cannot remove UniqueConstraint on ForeignKey with Django/MySQL

Django allows you to define custom UniqueConstraints to specify which combinations of values are allowed in a row, but removing these later can be problematic when some ForeignKey is involved, at least with MySQL it may throw a Cannot drop index '...': needed in a foreign key constraint at you.

The example below shows you how to resolve such a situation in 3 small individual migrations:

  • We start with an existing UniqueConstraint.
class MyModel(models.Model):
    other_model = models.ForeignKey("OtherModel", on_delete=models.CASCADE)
    name = models.CharField(Max_length=128)

    class Meta:
        constraints = (
            models.UniqueConstraint(
                fields=(
                    "other_model",
                    "name",
                ),
                name="unique_other_model_name",
            ),
        )
  • Step 1: Tell Django that we don't want an index and constraint on the ForeignKey.
class MyModel(models.Model):
    other_model = models.ForeignKey(
        "OtherModel", 
        on_delete=models.CASCADE,
        db_index=False,
        db_constraint=False,
    )
    name = models.CharField(max_length=128)

    class Meta:
        constraints = (
            models.UniqueConstraint(
                fields=(
                    "other_model",
                    "name",
                ),
                name="unique_other_model_name",
            ),
        )
$ python manage.py makemigrations
  • Step 2: Remove the custom UniqueConstraint.
class MyModel(models.Model):
    other_model = models.ForeignKey(
        "OtherModel", 
        on_delete=models.CASCADE,
        db_index=False,
        db_constraint=False,
    )
    name = models.CharField(Max_length=128)
$ python manage.py makemigrations
  • Step 3: Re-introduce the default behaviour of having an index and constraint on the ForeignKey.
class MyModel(models.Model):
    other_model = models.ForeignKey( "OtherModel", on_delete=models.CASCADE)
    name = models.CharField(Max_length=128)
$ python manage.py makemigrations

You'll end up with 3 migration files that can easily be combined into a single one before running this in production, to tidy things up.

Done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment