Opened 8 years ago

Closed 8 years ago

#25648 closed Uncategorized (invalid)

Unable to modify or delete unique_together Model constraint (PostgreSQL)

Reported by: Matthew Miller Owned by: nobody
Component: Uncategorized Version: 1.8
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I'm having the worst luck making any changes to a unique_together constraint on one of my Models. I want to preface this with the fact that this seems very similar to ticket #23614.

Here's how my model started out:

class Skill(models.Model):
    company = models.IntegerField(default=1)
    title = models.CharField(max_length=50)

    class Meta:
        unique_together = ['company', 'title']

I added a location ForeignKey field to the model, with null=True set so that I could create a custom migration afterwards to populate the new field. After creating the custom migration, I then ran makemigration after 1) removing the null=True constraint on the location field, 2) altering unique_together to swap out location for company, and 3) deleting the company field.

This is what I ended up with (and remains the ideal state of this Model):

class Skill(models.Model):
    location = models.ForeignKey(Location, related_name="skills")
    title = models.CharField(max_length=50)

    class Meta:
        unique_together = ['location', 'title']

And this is the migration that Django generated after I made all three changes to Skill:

    operations = [
        migrations.AlterField(
            model_name='skill',
            name='location',
            field=models.ForeignKey(related_name='skills', to='serverapp.Location'),
        ),
        migrations.AlterUniqueTogether(
            name='skill',
            unique_together=set([('location', 'title')]),
        ),
        migrations.RemoveField(
            model_name='skill',
            name='company',
        ),
    ]

This migration continually error'd out until I removed the AlterUniqueTogether migration, at which point it ran fine.

Unfortunately, this left me in a bad spot as I could no longer make any changes to unique_together because (paraphrasing a bit) "the company field no longer existed".

In an effort to rollback things somewhat, I added a company field back into the Skill Model as a simple IntegerField with default=1. I hoped that I could trick the migration system into a state in which it could modify unique_together to the desired relationship. This migration processed successfully, so now I had the following Model:

class Skill(models.Model):
    company = models.IntegerField(default=1)
    location = models.ForeignKey(Location, related_name="skills")
    title = models.CharField(max_length=50)

    class Meta:
        unique_together = ['location', 'title']

Since modifying the constraint kept failing to go through, I then decided to do away with the constraint altogether:

class Skill(models.Model):
    company = models.IntegerField(default=1)
    location = models.ForeignKey(Location, related_name="skills")
    title = models.CharField(max_length=50)

    class Meta:
        # unique_together = ['location', 'title']

This generated the following expected migration file:

    operations = [
        migrations.AlterUniqueTogether(
            name='skill',
            unique_together=set([]),
        ),
    ]

This is where I'm currently stuck. Attemping to migrate this change generates the following error:

Operations to perform:
  Synchronize unmigrated apps: rest_framework_swagger, rest_framework, corsheaders, messages, staticfiles
  Apply all migrations: authtoken, sessions, contenttypes, reversion, serverapp, auth, admin
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
  Installing custom SQL...
Running migrations:
  Rendering model states... DONE
  Applying serverapp.0059_auto_20151030_1823...Traceback (most recent call last):
  File "shiftserver\manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "C:\Python34\lib\site-packages\django\core\management\__init__.py", line 338, in execute_from_command_line
    utility.execute()
  File "C:\Python34\lib\site-packages\django\core\management\__init__.py", line 330, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "C:\Python34\lib\site-packages\django\core\management\base.py", line 390, in run_from_argv
    self.execute(*args, **cmd_options)
  File "C:\Python34\lib\site-packages\django\core\management\base.py", line 441, in execute
    output = self.handle(*args, **options)
  File "C:\Python34\lib\site-packages\django\core\management\commands\migrate.py", line 221, in handle
    executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial)
  File "C:\Python34\lib\site-packages\django\db\migrations\executor.py", line 110, in migrate
    self.apply_migration(states[migration], migration, fake=fake, fake_initial=fake_initial)
  File "C:\Python34\lib\site-packages\django\db\migrations\executor.py", line 147, in apply_migration
    state = migration.apply(state, schema_editor)
  File "C:\Python34\lib\site-packages\django\db\migrations\migration.py", line 115, in apply
    operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
  File "C:\Python34\lib\site-packages\django\db\migrations\operations\models.py", line 355, in database_forwards
    getattr(new_model._meta, self.option_name, set()),
  File "C:\Python34\lib\site-packages\django\db\backends\base\schema.py", line 322, in alter_unique_together
    ", ".join(columns),
ValueError: Found wrong number (0) of constraints for serverapp_skill(company, title)

I peeked at schema.py:322 and while it looks as though alter_unique_together() isn't designed to handle being passed in 0 parameters for unique_together, I'm not confident enough in my reading of self._constraint_names() to say that the issue exists somewhere in there.

Any ideas what might be going on? I googled the hell out of this issue but couldn't find anything beyond the ticket I mentioned above, and even that's specific to MySQL so it didn't seem to be of much help.

I've lost a couple of hours to this problem. I really hope it's not something stupid I'm trying to do...

Change History (4)

comment:1 by Shai Berger, 8 years ago

Hello,

You seem to have left out one of the most important pieces of the puzzle -- the exact error you encountered at first when trying to change the unique-together.

I'm referring to the line that says:

This migration continually error'd out until I removed the AlterUniqueTogether migration, at which point it ran fine.

What was the error?

All in all, you appear to have found a true problem in Django with respect to removal of all unique_together constraints, but other than that, the report is a little unclear.

comment:2 by Matthew Miller, 8 years ago

I was afraid you were going to ask for that. Unfortunately by the time I'd started writing this ticket I'd cleared my console and could no longer see the entire error message.

Fortunately Google knows everything about me and my past internet searches, so I was able to recover the following from one of my earlier searches when I started troubleshooting things:

"Found wrong number of constraints (2) for"

I believe the entire message read something like this:

Found wrong number of constraints (2) for serverapp_skill(company_id, title)

But before I go any further...

Just now, in an effort to recreate the above error message so I could get a verbatim quote, the migration actually worked. I restored the database from an earlier backup I'd taken last month, migrated up until I started experiencing issues, then made a migration that removed the company field, removed the null=True constraint, and updated unique_together to replace company with location.

The generated migration file looked exactly the same as I posted yesterday:

    operations = [
        migrations.AlterField(
            model_name='skill',
            name='location',
            field=models.ForeignKey(related_name='skills', to='serverapp.Location'),
        ),
        migrations.AlterUniqueTogether(
            name='skill',
            unique_together=set([('location', 'title')]),
        ),
        migrations.RemoveField(
            model_name='skill',
            name='company',
        ),
    ]

The only difference is that, when I went to migrate just now it went through without issue. The tables updated constraints and removed columns just fine. I even checked the unique_together constraint manually via pgAdmin III and verified that it updated to reference location instead of company.

I have no idea why it went through this time. I literally did the exact same thing this morning that I first attempted yesterday, and for some reason the migration just decided to work.

Now I'm worried about migrating my production server...

In any case, would it be useful to talk more about the original error message? Is there anything in there that might shed light on this mystery? I'm glad the migration went through this second time but I'm also scared that I have nothing to look out for in case this issue rears its ugly head again.

comment:3 by Tim Graham, 8 years ago

I'm afraid that unless you can provide steps to reproduce a problem, we won't be able to provide much help. It seems that somehow your database might have been missing constraints or something.

comment:4 by Matthew Miller, 8 years ago

Resolution: invalid
Status: newclosed

I'm going to go ahead and close this ticket. I ended up cloning my production server and successfully ran it through all of the migrations, so it seems that something was hosed within the old dev database itself.

Thanks for your time.

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