﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
23906	"""ValueError: Found wrong number (0) of constraints"" when migrating backwards with from alter_unique_together migration."	liavkoren	nobody	"We have a database that was created with django 1.6 and south. We've subsequently upgraded to 1.7.0, removed our south migrations and created django-migrations. 

One of our models contains a unique_together constraint that includes four of its attributes. When I attempt to backwards migrate the app containing this model I get ""ValueError: Found wrong number (0) of constraints for foo_model(foo, bar, baz, quux)"". 

I haven't been able to replicate this error by creating a test model -- it seems possible that this may be related to models created in django 1.6/south and then migrated backwards in 1.7. 

The problem seems to be in django.db.backends.schema._contraint_names, which is supposed to return a list of relevant constrains for a set of columns.

In order to do this, it is comparing an input set of columns against the result returned from a call to
BaseDatabaseSchemaEditor.connection.introspection.get_constraints and filtering the results. The first test in the filter loop is:

{{{#!python
    if column_names is None or column_names == infodict['columns']:
}}}
which is failing because it involves a comparison of lists which are in different orders:

{{{#!python
(Pdb++) column_names
[u'foo', u'bar', u'baz', u'quux']
(Pdb++) infodict['columns']
[u'baz', u'bar', u'quux', u'foo']
(Pdb++) infodict['columns'] == column_names
False
(Pdb++) sorted(infodict['columns']) == sorted(column_names)
True
}}}

I have been able to fix this and migrate backwards by changing the django.db.backends.schema.BaseDatabaseSchemaEditor._constraint_names method to compare sets rather than lists:

{{{#!python
    def _constraint_names(self, model, column_names=None, unique=None, primary_key=None, index=None, foreign_key=None, check=None):
        """"""
        Returns all constraint names matching the columns and conditions
        """"""
        # column_names = list(column_names) if column_names else None
        column_names = set(column_names) if column_names else None
        with self.connection.cursor() as cursor:
            constraints = self.connection.introspection.get_constraints(cursor, model._meta.db_table)
        result = []
        for name, infodict in constraints.items():
            # if column_names is None or column_names == infodict['columns']:
            if column_names is None or column_names == set(infodict['columns']):
                if unique is not None and infodict['unique'] != unique:
                    continue
                if primary_key is not None and infodict['primary_key'] != primary_key:
                    continue
                if index is not None and infodict['index'] != index:
                    continue
                if check is not None and infodict['check'] != check:
                    continue
                if foreign_key is not None and not infodict['foreign_key']:
                    continue
                result.append(name)
        return result
}}}

I'm happy to submit a PR and tests for this issue, but I expect it's going to be non-trivial to reproduce.
"	Bug	closed	Migrations	1.7	Normal	invalid	alter_unique_together migrations constraints valueError	info+coding@…	Unreviewed	0	0	0	0	0	0
