Ticket #12460: 12460-3.diff
File 12460-3.diff, 8.6 KB (added by , 13 years ago) |
---|
-
django/core/management/commands/inspectdb.py
diff --git a/django/core/management/commands/inspectdb.py b/django/core/management/commands/inspectdb.py index 8da0d7e..f87bd20 100644
a b class Command(NoArgsCommand): 51 51 indexes = connection.introspection.get_indexes(cursor, table_name) 52 52 except NotImplementedError: 53 53 indexes = {} 54 used_column_names = [] # Holds column names used in the table so far 54 55 for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)): 55 column_name = row[0]56 att_name = column_name.lower()57 56 comment_notes = [] # Holds Field notes, to be displayed in a Python comment. 58 57 extra_params = {} # Holds Field parameters such as 'db_column'. 58 column_name = row[0] 59 is_relation = i in relations 60 61 att_name, params, notes = self.normalize_col_name( 62 column_name, used_column_names, is_relation) 63 extra_params.update(params) 64 comment_notes.extend(notes) 59 65 60 # If the column name can't be used verbatim as a Python 61 # attribute, set the "db_column" for this Field. 62 if ' ' in att_name or '-' in att_name or keyword.iskeyword(att_name) or column_name != att_name: 63 extra_params['db_column'] = column_name 66 used_column_names.append(att_name) 64 67 65 68 # Add primary_key and unique, if necessary. 66 69 if column_name in indexes: … … class Command(NoArgsCommand): 69 72 elif indexes[column_name]['unique']: 70 73 extra_params['unique'] = True 71 74 72 # Modify the field name to make it Python-compatible. 73 if ' ' in att_name: 74 att_name = att_name.replace(' ', '_') 75 comment_notes.append('Field renamed to remove spaces.') 76 77 if '-' in att_name: 78 att_name = att_name.replace('-', '_') 79 comment_notes.append('Field renamed to remove dashes.') 80 81 if column_name != att_name: 82 comment_notes.append('Field name made lowercase.') 83 84 if i in relations: 75 if is_relation: 85 76 rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1]) 86 77 field_type = 'ForeignKey(%s' % rel_to 87 if att_name.endswith('_id'):88 att_name = att_name[:-3]89 else:90 extra_params['db_column'] = column_name91 78 else: 92 79 # Calling `get_field_type` to get the field type string and any 93 80 # additional paramters and notes. … … class Command(NoArgsCommand): 97 84 98 85 field_type += '(' 99 86 100 if keyword.iskeyword(att_name):101 att_name += '_field'102 comment_notes.append('Field renamed because it was a Python reserved word.')103 104 if att_name[0].isdigit():105 att_name = 'number_%s' % att_name106 extra_params['db_column'] = unicode(column_name)107 comment_notes.append("Field renamed because it wasn't a "108 "valid Python identifier.")109 110 87 # Don't output 'id = meta.AutoField(primary_key=True)', because 111 88 # that's assumed if it doesn't exist. 112 89 if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}: … … class Command(NoArgsCommand): 131 108 for meta_line in self.get_meta(table_name): 132 109 yield meta_line 133 110 111 def normalize_col_name(self, col_name, used_column_names, is_relation): 112 """Modify the column name to make it Python-compatible as a field name""" 113 field_params = {} 114 field_notes = [] 115 116 new_name = col_name.lower() 117 if new_name != col_name: 118 field_notes.append('Field name made lowercase.') 119 120 if is_relation: 121 if new_name.endswith('_id'): 122 new_name = new_name[:-3] 123 else: 124 field_params['db_column'] = col_name 125 126 if ' ' in new_name: 127 new_name = new_name.replace(' ', '_') 128 field_notes.append('Field renamed to remove spaces.') 129 130 if '-' in new_name: 131 new_name = new_name.replace('-', '_') 132 field_notes.append('Field renamed to remove dashes.') 133 134 if new_name.find('__') >= 0: 135 while new_name.find('__') >= 0: 136 new_name = new_name.replace('__', '_') 137 field_notes.append("Field renamed because it contained more than one '_' in a row.") 138 139 if new_name.startswith('_'): 140 new_name = 'field%s' % new_name 141 field_notes.append("Field renamed because it started with '_'.") 142 143 if new_name.endswith('_'): 144 new_name = '%sfield' % new_name 145 field_notes.append("Field renamed because it ended with '_'.") 146 147 if keyword.iskeyword(new_name): 148 new_name += '_field' 149 field_notes.append('Field renamed because it was a Python reserved word.') 150 151 if new_name[0].isdigit(): 152 new_name = 'number_%s' % new_name 153 field_notes.append("Field renamed because it wasn't a " 154 "valid Python identifier.") 155 156 if new_name in used_column_names: 157 num = 0 158 while '%s_%d' % (new_name, num) in used_column_names: 159 num += 1 160 new_name = '%s_%d' % (new_name, num) 161 field_notes.append('Field renamed because of name conflict.') 162 163 if col_name != new_name and field_notes: 164 field_params['db_column'] = unicode(col_name) 165 166 return new_name, field_params, field_notes 167 134 168 def get_field_type(self, connection, table_name, row): 135 169 """ 136 170 Given the database connection, the table name, and the cursor row -
tests/regressiontests/inspectdb/models.py
diff --git a/tests/regressiontests/inspectdb/models.py b/tests/regressiontests/inspectdb/models.py index 9f81585..352053a 100644
a b class DigitsInColumnName(models.Model): 19 19 all_digits = models.CharField(max_length=11, db_column='123') 20 20 leading_digit = models.CharField(max_length=11, db_column='4extra') 21 21 leading_digits = models.CharField(max_length=11, db_column='45extra') 22 23 class UnderscoresInColumnName(models.Model): 24 field = models.IntegerField(db_column='field') 25 field_field_0 = models.IntegerField(db_column='Field_') 26 field_field_1 = models.IntegerField(db_column='Field__') 27 field_field_2 = models.IntegerField(db_column='__field') -
tests/regressiontests/inspectdb/tests.py
diff --git a/tests/regressiontests/inspectdb/tests.py b/tests/regressiontests/inspectdb/tests.py index 944eb64..ea6cc2b 100644
a b class InspectDBTestCase(TestCase): 12 12 call_command('inspectdb', stdout=out) 13 13 error_message = "inspectdb generated an attribute name which is a python keyword" 14 14 self.assertNotIn("from = models.ForeignKey(InspectdbPeople)", out.getvalue(), msg=error_message) 15 self.assertIn("from_field = models.ForeignKey(InspectdbPeople )", out.getvalue())15 self.assertIn("from_field = models.ForeignKey(InspectdbPeople, db_column=u'from_id')", out.getvalue()) 16 16 self.assertIn("people_pk = models.ForeignKey(InspectdbPeople, primary_key=True)", 17 17 out.getvalue()) 18 18 self.assertIn("people_unique = models.ForeignKey(InspectdbPeople, unique=True)", … … class InspectDBTestCase(TestCase): 33 33 34 34 self.assertNotIn(" 45extra = models.CharField", out.getvalue(), msg=error_message) 35 35 self.assertIn("number_45extra = models.CharField", out.getvalue()) 36 37 def test_underscores_column_name_introspection(self): 38 """Introspection of column names containing underscores (#12460)""" 39 out = StringIO() 40 call_command('inspectdb', stdout=out) 41 self.assertIn("field = models.IntegerField()", out.getvalue()) 42 self.assertIn("field_field = models.IntegerField(db_column=u'Field_')", out.getvalue()) 43 self.assertIn("field_field_0 = models.IntegerField(db_column=u'Field__')", out.getvalue()) 44 self.assertIn("field_field_1 = models.IntegerField(db_column=u'__field')", out.getvalue())