diff --git a/AUTHORS b/AUTHORS index cf632452c6..e62c079413 100644 --- a/AUTHORS +++ b/AUTHORS @@ -567,6 +567,7 @@ answer newbie questions, and generally made Django that much better: Michael Placentra II Michael Radziej Michael Schwarz + Michael Sinov Michael Thornhill Michal Chruszcz michal@plovarna.cz diff --git a/django/core/management/commands/inspectdb.py b/django/core/management/commands/inspectdb.py index 31c4ae062b..c84ce14739 100644 --- a/django/core/management/commands/inspectdb.py +++ b/django/core/management/commands/inspectdb.py @@ -269,16 +269,24 @@ class Command(BaseCommand): to the given database table name. """ unique_together = [] + has_unsupported_constraint = False for params in constraints.values(): if params['unique']: columns = params['columns'] + if None in columns: + has_unsupported_constraint = True + columns = [x for x in columns if x is not None] if len(columns) > 1: unique_together.append(str(tuple(column_to_field_name[c] for c in columns))) managed_comment = " # Created from a view. Don't remove." if is_view else "" - meta = ["", - " class Meta:", - " managed = False%s" % managed_comment, - " db_table = '%s'" % table_name] + meta = [''] + if has_unsupported_constraint: + meta.append(' # A unique constraint could not be introspected.') + meta += [ + ' class Meta:', + ' managed = False%s' % managed_comment, + ' db_table = %r' % table_name + ] if unique_together: tup = '(' + ', '.join(unique_together) + ',)' meta += [" unique_together = %s" % tup] diff --git a/tests/inspectdb/models.py b/tests/inspectdb/models.py index 8c660658cd..20f6b5e940 100644 --- a/tests/inspectdb/models.py +++ b/tests/inspectdb/models.py @@ -17,6 +17,7 @@ class PeopleData(models.Model): class PeopleMoreData(models.Model): people_unique = models.ForeignKey(People, models.CASCADE, unique=True) + message = models.ForeignKey(Message, models.CASCADE, blank=True, null=True) license = models.CharField(max_length=255) diff --git a/tests/inspectdb/tests.py b/tests/inspectdb/tests.py index b6bbf0b2dd..e994b2cb74 100644 --- a/tests/inspectdb/tests.py +++ b/tests/inspectdb/tests.py @@ -7,6 +7,8 @@ from django.db import connection from django.db.backends.base.introspection import TableInfo from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature +from .models import PeopleMoreData + def inspectdb_tables_only(table_name): """ @@ -21,6 +23,7 @@ def special_table_only(table_name): class InspectDBTestCase(TestCase): + unique_re = re.compile(r'.*unique_together = \((.+),\).*') def test_stealth_table_name_filter_option(self): out = StringIO() @@ -212,8 +215,7 @@ class InspectDBTestCase(TestCase): call_command('inspectdb', 'inspectdb_uniquetogether', stdout=out) output = out.getvalue() self.assertIn(" unique_together = (('", output) - unique_re = re.compile(r'.*unique_together = \((.+),\).*') - unique_together_match = re.findall(unique_re, output) + unique_together_match = re.findall(self.unique_re, output) # There should be one unique_together tuple. self.assertEqual(len(unique_together_match), 1) fields = unique_together_match[0] @@ -225,6 +227,28 @@ class InspectDBTestCase(TestCase): # are given an integer suffix. self.assertIn("('non_unique_column', 'non_unique_column_0')", fields) + @skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific SQL') + def test_unsupported_unique_together(self): + """Unsupported index types (COALESCE here) are skipped.""" + with connection.cursor() as c: + c.execute( + 'CREATE UNIQUE INDEX Findex ON %s ' + '(id, people_unique_id, COALESCE(message_id, -1))' % PeopleMoreData._meta.db_table + ) + try: + out = StringIO() + call_command( + 'inspectdb', + table_name_filter=lambda tn: tn.startswith(PeopleMoreData._meta.db_table), + stdout=out, + ) + output = out.getvalue() + self.assertIn('# A unique constraint could not be introspected.', output) + self.assertEqual(re.findall(self.unique_re, output), ["('id', 'people_unique')"]) + finally: + with connection.cursor() as c: + c.execute('DROP INDEX Findex') + @skipUnless(connection.vendor == 'sqlite', "Only patched sqlite's DatabaseIntrospection.data_types_reverse for this test") def test_custom_fields(self):