Ticket #12460: 12460-4.diff
File 12460-4.diff, 8.8 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 af2da45..54b35f2 100644
a b class Command(NoArgsCommand): 53 53 indexes = connection.introspection.get_indexes(cursor, table_name) 54 54 except NotImplementedError: 55 55 indexes = {} 56 used_column_names = [] # Holds column names used in the table so far 56 57 for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)): 57 column_name = row[0]58 att_name = column_name.lower()59 58 comment_notes = [] # Holds Field notes, to be displayed in a Python comment. 60 59 extra_params = {} # Holds Field parameters such as 'db_column'. 60 column_name = row[0] 61 is_relation = i in relations 61 62 62 # If the column name can't be used verbatim as a Python 63 # attribute, set the "db_column" for this Field. 64 if ' ' in att_name or '-' in att_name or keyword.iskeyword(att_name) or column_name != att_name: 65 extra_params['db_column'] = column_name 63 att_name, params, notes = self.normalize_col_name( 64 column_name, used_column_names, is_relation) 65 extra_params.update(params) 66 comment_notes.extend(notes) 67 68 used_column_names.append(att_name) 66 69 67 70 # Add primary_key and unique, if necessary. 68 71 if column_name in indexes: … … class Command(NoArgsCommand): 71 74 elif indexes[column_name]['unique']: 72 75 extra_params['unique'] = True 73 76 74 # Modify the field name to make it Python-compatible. 75 if ' ' in att_name: 76 att_name = att_name.replace(' ', '_') 77 comment_notes.append('Field renamed to remove spaces.') 78 79 if '-' in att_name: 80 att_name = att_name.replace('-', '_') 81 comment_notes.append('Field renamed to remove dashes.') 82 83 if column_name != att_name: 84 comment_notes.append('Field name made lowercase.') 85 86 if i in relations: 77 if is_relation: 87 78 rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1]) 88 89 79 if rel_to in known_models: 90 80 field_type = 'ForeignKey(%s' % rel_to 91 81 else: 92 82 field_type = "ForeignKey('%s'" % rel_to 93 94 if att_name.endswith('_id'):95 att_name = att_name[:-3]96 else:97 extra_params['db_column'] = column_name98 83 else: 99 84 # Calling `get_field_type` to get the field type string and any 100 85 # additional paramters and notes. … … class Command(NoArgsCommand): 104 89 105 90 field_type += '(' 106 91 107 if keyword.iskeyword(att_name):108 att_name += '_field'109 comment_notes.append('Field renamed because it was a Python reserved word.')110 111 if att_name[0].isdigit():112 att_name = 'number_%s' % att_name113 extra_params['db_column'] = unicode(column_name)114 comment_notes.append("Field renamed because it wasn't a "115 "valid Python identifier.")116 117 92 # Don't output 'id = meta.AutoField(primary_key=True)', because 118 93 # that's assumed if it doesn't exist. 119 94 if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}: … … class Command(NoArgsCommand): 138 113 for meta_line in self.get_meta(table_name): 139 114 yield meta_line 140 115 116 def normalize_col_name(self, col_name, used_column_names, is_relation): 117 """Modify the column name to make it Python-compatible as a field name""" 118 field_params = {} 119 field_notes = [] 120 121 new_name = col_name.lower() 122 if new_name != col_name: 123 field_notes.append('Field name made lowercase.') 124 125 if is_relation: 126 if new_name.endswith('_id'): 127 new_name = new_name[:-3] 128 else: 129 field_params['db_column'] = col_name 130 131 if ' ' in new_name: 132 new_name = new_name.replace(' ', '_') 133 field_notes.append('Field renamed to remove spaces.') 134 135 if '-' in new_name: 136 new_name = new_name.replace('-', '_') 137 field_notes.append('Field renamed to remove dashes.') 138 139 if new_name.find('__') >= 0: 140 while new_name.find('__') >= 0: 141 new_name = new_name.replace('__', '_') 142 field_notes.append("Field renamed because it contained more than one '_' in a row.") 143 144 if new_name.startswith('_'): 145 new_name = 'field%s' % new_name 146 field_notes.append("Field renamed because it started with '_'.") 147 148 if new_name.endswith('_'): 149 new_name = '%sfield' % new_name 150 field_notes.append("Field renamed because it ended with '_'.") 151 152 if keyword.iskeyword(new_name): 153 new_name += '_field' 154 field_notes.append('Field renamed because it was a Python reserved word.') 155 156 if new_name[0].isdigit(): 157 new_name = 'number_%s' % new_name 158 field_notes.append("Field renamed because it wasn't a " 159 "valid Python identifier.") 160 161 if new_name in used_column_names: 162 num = 0 163 while '%s_%d' % (new_name, num) in used_column_names: 164 num += 1 165 new_name = '%s_%d' % (new_name, num) 166 field_notes.append('Field renamed because of name conflict.') 167 168 if col_name != new_name and field_notes: 169 field_params['db_column'] = unicode(col_name) 170 171 return new_name, field_params, field_notes 172 141 173 def get_field_type(self, connection, table_name, row): 142 174 """ 143 175 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 6896bf9..41dfbb4 100644
a b class InspectDBTestCase(TestCase): 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 15 # As InspectdbPeople model is defined after InspectdbMessage, it should be quoted 16 self.assertIn("from_field = models.ForeignKey('InspectdbPeople' )", out.getvalue())16 self.assertIn("from_field = models.ForeignKey('InspectdbPeople', db_column=u'from_id')", out.getvalue()) 17 17 self.assertIn("people_pk = models.ForeignKey(InspectdbPeople, primary_key=True)", 18 18 out.getvalue()) 19 19 self.assertIn("people_unique = models.ForeignKey(InspectdbPeople, unique=True)", … … class InspectDBTestCase(TestCase): 34 34 35 35 self.assertNotIn(" 45extra = models.CharField", out.getvalue(), msg=error_message) 36 36 self.assertIn("number_45extra = models.CharField", out.getvalue()) 37 38 def test_underscores_column_name_introspection(self): 39 """Introspection of column names containing underscores (#12460)""" 40 out = StringIO() 41 call_command('inspectdb', stdout=out) 42 self.assertIn("field = models.IntegerField()", out.getvalue()) 43 self.assertIn("field_field = models.IntegerField(db_column=u'Field_')", out.getvalue()) 44 self.assertIn("field_field_0 = models.IntegerField(db_column=u'Field__')", out.getvalue()) 45 self.assertIn("field_field_1 = models.IntegerField(db_column=u'__field')", out.getvalue())