Loading django/core/management/commands/inspectdb.py +5 −3 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ class Command(BaseCommand): except NotImplementedError: constraints = {} used_column_names = [] # Holds column names used in the table so far column_to_field_name = {} # Maps column names to names of model fields for row in connection.introspection.get_table_description(cursor, table_name): comment_notes = [] # Holds Field notes, to be displayed in a Python comment. extra_params = OrderedDict() # Holds Field parameters such as 'db_column'. Loading @@ -83,6 +84,7 @@ class Command(BaseCommand): comment_notes.extend(notes) used_column_names.append(att_name) column_to_field_name[column_name] = att_name # Add primary_key and unique, if necessary. if column_name in indexes: Loading Loading @@ -145,7 +147,7 @@ class Command(BaseCommand): if comment_notes: field_desc += ' # ' + ' '.join(comment_notes) yield ' %s' % field_desc for meta_line in self.get_meta(table_name, constraints): for meta_line in self.get_meta(table_name, constraints, column_to_field_name): yield meta_line def normalize_col_name(self, col_name, used_column_names, is_relation): Loading Loading @@ -242,7 +244,7 @@ class Command(BaseCommand): return field_type, field_params, field_notes def get_meta(self, table_name, constraints): def get_meta(self, table_name, constraints, column_to_field_name): """ Return a sequence comprising the lines of code necessary to construct the inner Meta class for the model corresponding Loading @@ -255,7 +257,7 @@ class Command(BaseCommand): if len(columns) > 1: # we do not want to include the u"" or u'' prefix # so we build the string rather than interpolate the tuple tup = '(' + ', '.join("'%s'" % c for c in columns) + ')' tup = '(' + ', '.join("'%s'" % column_to_field_name[c] for c in columns) + ')' unique_together.append(tup) meta = ["", " class Meta:", Loading docs/releases/1.8.8.txt +2 −1 Original line number Diff line number Diff line Loading @@ -9,4 +9,5 @@ Django 1.8.8 fixes several bugs in 1.8.7. Bugfixes ======== * ... * Fixed incorrect ``unique_together`` field name generation by ``inspectdb`` (:ticket:`25274`). tests/inspectdb/models.py +8 −1 Original line number Diff line number Diff line Loading @@ -72,6 +72,13 @@ class ColumnTypes(models.Model): class UniqueTogether(models.Model): field1 = models.IntegerField() field2 = models.CharField(max_length=10) from_field = models.IntegerField(db_column='from') non_unique = models.IntegerField(db_column='non__unique_column') non_unique_0 = models.IntegerField(db_column='non_unique__column') class Meta: unique_together = ('field1', 'field2') unique_together = [ ('field1', 'field2'), ('from_field', 'field1'), ('non_unique', 'non_unique_0'), ] tests/inspectdb/tests.py +12 −4 Original line number Diff line number Diff line Loading @@ -234,10 +234,18 @@ class InspectDBTestCase(TestCase): table_name_filter=lambda tn: tn.startswith('inspectdb_uniquetogether'), stdout=out) output = out.getvalue() self.assertIn( " unique_together = (('field1', 'field2'),)", output, msg='inspectdb should generate unique_together.' ) unique_re = re.compile(r'.*unique_together = \((.+),\).*') unique_together_match = re.findall(unique_re, output) # There should be one unique_together tuple. self.assertEqual(len(unique_together_match), 1) fields = unique_together_match[0] # Fields with db_column = field name. self.assertIn("('field1', 'field2')", fields) # Fields from columns whose names are Python keywords. self.assertIn("('field1', 'field2')", fields) # Fields whose names normalize to the same Python field name and hence # are given an integer suffix. self.assertIn("('non_unique_column', 'non_unique_column_0')", fields) @skipUnless(connection.vendor == 'sqlite', "Only patched sqlite's DatabaseIntrospection.data_types_reverse for this test") Loading Loading
django/core/management/commands/inspectdb.py +5 −3 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ class Command(BaseCommand): except NotImplementedError: constraints = {} used_column_names = [] # Holds column names used in the table so far column_to_field_name = {} # Maps column names to names of model fields for row in connection.introspection.get_table_description(cursor, table_name): comment_notes = [] # Holds Field notes, to be displayed in a Python comment. extra_params = OrderedDict() # Holds Field parameters such as 'db_column'. Loading @@ -83,6 +84,7 @@ class Command(BaseCommand): comment_notes.extend(notes) used_column_names.append(att_name) column_to_field_name[column_name] = att_name # Add primary_key and unique, if necessary. if column_name in indexes: Loading Loading @@ -145,7 +147,7 @@ class Command(BaseCommand): if comment_notes: field_desc += ' # ' + ' '.join(comment_notes) yield ' %s' % field_desc for meta_line in self.get_meta(table_name, constraints): for meta_line in self.get_meta(table_name, constraints, column_to_field_name): yield meta_line def normalize_col_name(self, col_name, used_column_names, is_relation): Loading Loading @@ -242,7 +244,7 @@ class Command(BaseCommand): return field_type, field_params, field_notes def get_meta(self, table_name, constraints): def get_meta(self, table_name, constraints, column_to_field_name): """ Return a sequence comprising the lines of code necessary to construct the inner Meta class for the model corresponding Loading @@ -255,7 +257,7 @@ class Command(BaseCommand): if len(columns) > 1: # we do not want to include the u"" or u'' prefix # so we build the string rather than interpolate the tuple tup = '(' + ', '.join("'%s'" % c for c in columns) + ')' tup = '(' + ', '.join("'%s'" % column_to_field_name[c] for c in columns) + ')' unique_together.append(tup) meta = ["", " class Meta:", Loading
docs/releases/1.8.8.txt +2 −1 Original line number Diff line number Diff line Loading @@ -9,4 +9,5 @@ Django 1.8.8 fixes several bugs in 1.8.7. Bugfixes ======== * ... * Fixed incorrect ``unique_together`` field name generation by ``inspectdb`` (:ticket:`25274`).
tests/inspectdb/models.py +8 −1 Original line number Diff line number Diff line Loading @@ -72,6 +72,13 @@ class ColumnTypes(models.Model): class UniqueTogether(models.Model): field1 = models.IntegerField() field2 = models.CharField(max_length=10) from_field = models.IntegerField(db_column='from') non_unique = models.IntegerField(db_column='non__unique_column') non_unique_0 = models.IntegerField(db_column='non_unique__column') class Meta: unique_together = ('field1', 'field2') unique_together = [ ('field1', 'field2'), ('from_field', 'field1'), ('non_unique', 'non_unique_0'), ]
tests/inspectdb/tests.py +12 −4 Original line number Diff line number Diff line Loading @@ -234,10 +234,18 @@ class InspectDBTestCase(TestCase): table_name_filter=lambda tn: tn.startswith('inspectdb_uniquetogether'), stdout=out) output = out.getvalue() self.assertIn( " unique_together = (('field1', 'field2'),)", output, msg='inspectdb should generate unique_together.' ) unique_re = re.compile(r'.*unique_together = \((.+),\).*') unique_together_match = re.findall(unique_re, output) # There should be one unique_together tuple. self.assertEqual(len(unique_together_match), 1) fields = unique_together_match[0] # Fields with db_column = field name. self.assertIn("('field1', 'field2')", fields) # Fields from columns whose names are Python keywords. self.assertIn("('field1', 'field2')", fields) # Fields whose names normalize to the same Python field name and hence # are given an integer suffix. self.assertIn("('non_unique_column', 'non_unique_column_0')", fields) @skipUnless(connection.vendor == 'sqlite', "Only patched sqlite's DatabaseIntrospection.data_types_reverse for this test") Loading