diff -r 8d941af65caf -r 77b6da96e6f1 web/lib/django/core/management/commands/inspectdb.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/lib/django/core/management/commands/inspectdb.py Wed Jun 02 18:57:35 2010 +0200 @@ -0,0 +1,164 @@ +import keyword +from optparse import make_option + +from django.core.management.base import NoArgsCommand, CommandError +from django.db import connections, DEFAULT_DB_ALIAS + +class Command(NoArgsCommand): + help = "Introspects the database tables in the given database and outputs a Django model module." + + option_list = NoArgsCommand.option_list + ( + make_option('--database', action='store', dest='database', + default=DEFAULT_DB_ALIAS, help='Nominates a database to ' + 'introspect. Defaults to using the "default" database.'), + ) + + requires_model_validation = False + + db_module = 'django.db' + + def handle_noargs(self, **options): + try: + for line in self.handle_inspection(options): + print line + except NotImplementedError: + raise CommandError("Database inspection isn't supported for the currently selected database backend.") + + def handle_inspection(self, options): + connection = connections[options.get('database', DEFAULT_DB_ALIAS)] + + table2model = lambda table_name: table_name.title().replace('_', '').replace(' ', '').replace('-', '') + + cursor = connection.cursor() + yield "# This is an auto-generated Django model module." + yield "# You'll have to do the following manually to clean this up:" + yield "# * Rearrange models' order" + yield "# * Make sure each model has one field with primary_key=True" + yield "# Feel free to rename the models, but don't rename db_table values or field names." + yield "#" + yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'" + yield "# into your database." + yield '' + yield 'from %s import models' % self.db_module + yield '' + for table_name in connection.introspection.get_table_list(cursor): + yield 'class %s(models.Model):' % table2model(table_name) + try: + relations = connection.introspection.get_relations(cursor, table_name) + except NotImplementedError: + relations = {} + try: + indexes = connection.introspection.get_indexes(cursor, table_name) + except NotImplementedError: + indexes = {} + for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)): + column_name = row[0] + att_name = column_name.lower() + comment_notes = [] # Holds Field notes, to be displayed in a Python comment. + extra_params = {} # Holds Field parameters such as 'db_column'. + + # If the column name can't be used verbatim as a Python + # attribute, set the "db_column" for this Field. + if ' ' in att_name or '-' in att_name or keyword.iskeyword(att_name) or column_name != att_name: + extra_params['db_column'] = column_name + + # Modify the field name to make it Python-compatible. + if ' ' in att_name: + att_name = att_name.replace(' ', '_') + comment_notes.append('Field renamed to remove spaces.') + if '-' in att_name: + att_name = att_name.replace('-', '_') + comment_notes.append('Field renamed to remove dashes.') + if keyword.iskeyword(att_name): + att_name += '_field' + comment_notes.append('Field renamed because it was a Python reserved word.') + if column_name != att_name: + comment_notes.append('Field name made lowercase.') + + if i in relations: + rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1]) + field_type = 'ForeignKey(%s' % rel_to + if att_name.endswith('_id'): + att_name = att_name[:-3] + else: + extra_params['db_column'] = column_name + else: + # Calling `get_field_type` to get the field type string and any + # additional paramters and notes. + field_type, field_params, field_notes = self.get_field_type(connection, table_name, row) + extra_params.update(field_params) + comment_notes.extend(field_notes) + + # Add primary_key and unique, if necessary. + if column_name in indexes: + if indexes[column_name]['primary_key']: + extra_params['primary_key'] = True + elif indexes[column_name]['unique']: + extra_params['unique'] = True + + field_type += '(' + + # Don't output 'id = meta.AutoField(primary_key=True)', because + # that's assumed if it doesn't exist. + if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}: + continue + + # Add 'null' and 'blank', if the 'null_ok' flag was present in the + # table description. + if row[6]: # If it's NULL... + extra_params['blank'] = True + if not field_type in ('TextField(', 'CharField('): + extra_params['null'] = True + + field_desc = '%s = models.%s' % (att_name, field_type) + if extra_params: + if not field_desc.endswith('('): + field_desc += ', ' + field_desc += ', '.join(['%s=%r' % (k, v) for k, v in extra_params.items()]) + field_desc += ')' + if comment_notes: + field_desc += ' # ' + ' '.join(comment_notes) + yield ' %s' % field_desc + for meta_line in self.get_meta(table_name): + yield meta_line + + def get_field_type(self, connection, table_name, row): + """ + Given the database connection, the table name, and the cursor row + description, this routine will return the given field type name, as + well as any additional keyword parameters and notes for the field. + """ + field_params = {} + field_notes = [] + + try: + field_type = connection.introspection.get_field_type(row[1], row) + except KeyError: + field_type = 'TextField' + field_notes.append('This field type is a guess.') + + # This is a hook for DATA_TYPES_REVERSE to return a tuple of + # (field_type, field_params_dict). + if type(field_type) is tuple: + field_type, new_params = field_type + field_params.update(new_params) + + # Add max_length for all CharFields. + if field_type == 'CharField' and row[3]: + field_params['max_length'] = row[3] + + if field_type == 'DecimalField': + field_params['max_digits'] = row[4] + field_params['decimal_places'] = row[5] + + return field_type, field_params, field_notes + + def get_meta(self, table_name): + """ + Return a sequence comprising the lines of code necessary + to construct the inner Meta class for the model corresponding + to the given database table name. + """ + return [' class Meta:', + ' db_table = %r' % table_name, + '']