app/django/core/management/commands/inspectdb.py
changeset 54 03e267d67478
child 323 ff1a9aa48cfd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/django/core/management/commands/inspectdb.py	Fri Jul 18 18:22:23 2008 +0000
@@ -0,0 +1,120 @@
+from django.core.management.base import NoArgsCommand, CommandError
+
+class Command(NoArgsCommand):
+    help = "Introspects the database tables in the given database and outputs a Django model module."
+
+    requires_model_validation = False
+
+    def handle_noargs(self, **options):
+        try:
+            for line in self.handle_inspection():
+                print line
+        except NotImplementedError:
+            raise CommandError("Database inspection isn't supported for the currently selected database backend.")
+
+    def handle_inspection(self):
+        from django.db import connection, get_introspection_module
+        import keyword
+
+        introspection_module = get_introspection_module()
+
+        table2model = lambda table_name: table_name.title().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 django.db import models'
+        yield ''
+        for table_name in introspection_module.get_table_list(cursor):
+            yield 'class %s(models.Model):' % table2model(table_name)
+            try:
+                relations = introspection_module.get_relations(cursor, table_name)
+            except NotImplementedError:
+                relations = {}
+            try:
+                indexes = introspection_module.get_indexes(cursor, table_name)
+            except NotImplementedError:
+                indexes = {}
+            for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)):
+                att_name = row[0].lower()
+                comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
+                extra_params = {}  # Holds Field parameters such as 'db_column'.
+
+                if ' ' in att_name:
+                    extra_params['db_column'] = att_name
+                    att_name = att_name.replace(' ', '')
+                    comment_notes.append('Field renamed to remove spaces.')
+                if keyword.iskeyword(att_name):
+                    extra_params['db_column'] = att_name
+                    att_name += '_field'
+                    comment_notes.append('Field renamed because it was a Python reserved word.')
+
+                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'] = att_name
+                else:
+                    try:
+                        field_type = introspection_module.DATA_TYPES_REVERSE[row[1]]
+                    except KeyError:
+                        field_type = 'TextField'
+                        comment_notes.append('This field type is a guess.')
+
+                    # This is a hook for DATA_TYPES_REVERSE to return a tuple of
+                    # (field_type, extra_params_dict).
+                    if type(field_type) is tuple:
+                        field_type, new_params = field_type
+                        extra_params.update(new_params)
+
+                    # Add max_length for all CharFields.
+                    if field_type == 'CharField' and row[3]:
+                        extra_params['max_length'] = row[3]
+
+                    if field_type == 'DecimalField':
+                        extra_params['max_digits'] = row[4]
+                        extra_params['decimal_places'] = row[5]
+
+                    # Add primary_key and unique, if necessary.
+                    column_name = extra_params.get('db_column', att_name)
+                    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
+            yield '    class Meta:'
+            yield '        db_table = %r' % table_name
+            yield ''