web/lib/django/core/management/commands/inspectdb.py
changeset 0 0d40e90630ef
child 29 cc9b7e14412b
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 from django.core.management.base import NoArgsCommand, CommandError
       
     2 
       
     3 class Command(NoArgsCommand):
       
     4     help = "Introspects the database tables in the given database and outputs a Django model module."
       
     5 
       
     6     requires_model_validation = False
       
     7 
       
     8     def handle_noargs(self, **options):
       
     9         try:
       
    10             for line in self.handle_inspection():
       
    11                 print line
       
    12         except NotImplementedError:
       
    13             raise CommandError("Database inspection isn't supported for the currently selected database backend.")
       
    14 
       
    15     def handle_inspection(self):
       
    16         from django.db import connection
       
    17         import keyword
       
    18 
       
    19         table2model = lambda table_name: table_name.title().replace('_', '').replace(' ', '').replace('-', '')
       
    20 
       
    21         cursor = connection.cursor()
       
    22         yield "# This is an auto-generated Django model module."
       
    23         yield "# You'll have to do the following manually to clean this up:"
       
    24         yield "#     * Rearrange models' order"
       
    25         yield "#     * Make sure each model has one field with primary_key=True"
       
    26         yield "# Feel free to rename the models, but don't rename db_table values or field names."
       
    27         yield "#"
       
    28         yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'"
       
    29         yield "# into your database."
       
    30         yield ''
       
    31         yield 'from django.db import models'
       
    32         yield ''
       
    33         for table_name in connection.introspection.get_table_list(cursor):
       
    34             yield 'class %s(models.Model):' % table2model(table_name)
       
    35             try:
       
    36                 relations = connection.introspection.get_relations(cursor, table_name)
       
    37             except NotImplementedError:
       
    38                 relations = {}
       
    39             try:
       
    40                 indexes = connection.introspection.get_indexes(cursor, table_name)
       
    41             except NotImplementedError:
       
    42                 indexes = {}
       
    43             for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
       
    44                 column_name = row[0]
       
    45                 att_name = column_name.lower()
       
    46                 comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
       
    47                 extra_params = {}  # Holds Field parameters such as 'db_column'.
       
    48 
       
    49                 # If the column name can't be used verbatim as a Python
       
    50                 # attribute, set the "db_column" for this Field.
       
    51                 if ' ' in att_name or '-' in att_name or keyword.iskeyword(att_name) or column_name != att_name:
       
    52                     extra_params['db_column'] = column_name
       
    53 
       
    54                 # Modify the field name to make it Python-compatible.
       
    55                 if ' ' in att_name:
       
    56                     att_name = att_name.replace(' ', '_')
       
    57                     comment_notes.append('Field renamed to remove spaces.')
       
    58                 if '-' in att_name:
       
    59                     att_name = att_name.replace('-', '_')
       
    60                     comment_notes.append('Field renamed to remove dashes.')
       
    61                 if keyword.iskeyword(att_name):
       
    62                     att_name += '_field'
       
    63                     comment_notes.append('Field renamed because it was a Python reserved word.')
       
    64                 if column_name != att_name:
       
    65                     comment_notes.append('Field name made lowercase.')
       
    66 
       
    67                 if i in relations:
       
    68                     rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1])
       
    69                     field_type = 'ForeignKey(%s' % rel_to
       
    70                     if att_name.endswith('_id'):
       
    71                         att_name = att_name[:-3]
       
    72                     else:
       
    73                         extra_params['db_column'] = column_name
       
    74                 else:
       
    75                     try:
       
    76                         field_type = connection.introspection.get_field_type(row[1], row)
       
    77                     except KeyError:
       
    78                         field_type = 'TextField'
       
    79                         comment_notes.append('This field type is a guess.')
       
    80 
       
    81                     # This is a hook for DATA_TYPES_REVERSE to return a tuple of
       
    82                     # (field_type, extra_params_dict).
       
    83                     if type(field_type) is tuple:
       
    84                         field_type, new_params = field_type
       
    85                         extra_params.update(new_params)
       
    86 
       
    87                     # Add max_length for all CharFields.
       
    88                     if field_type == 'CharField' and row[3]:
       
    89                         extra_params['max_length'] = row[3]
       
    90 
       
    91                     if field_type == 'DecimalField':
       
    92                         extra_params['max_digits'] = row[4]
       
    93                         extra_params['decimal_places'] = row[5]
       
    94 
       
    95                     # Add primary_key and unique, if necessary.
       
    96                     if column_name in indexes:
       
    97                         if indexes[column_name]['primary_key']:
       
    98                             extra_params['primary_key'] = True
       
    99                         elif indexes[column_name]['unique']:
       
   100                             extra_params['unique'] = True
       
   101 
       
   102                     field_type += '('
       
   103 
       
   104                 # Don't output 'id = meta.AutoField(primary_key=True)', because
       
   105                 # that's assumed if it doesn't exist.
       
   106                 if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}:
       
   107                     continue
       
   108 
       
   109                 # Add 'null' and 'blank', if the 'null_ok' flag was present in the
       
   110                 # table description.
       
   111                 if row[6]: # If it's NULL...
       
   112                     extra_params['blank'] = True
       
   113                     if not field_type in ('TextField(', 'CharField('):
       
   114                         extra_params['null'] = True
       
   115 
       
   116                 field_desc = '%s = models.%s' % (att_name, field_type)
       
   117                 if extra_params:
       
   118                     if not field_desc.endswith('('):
       
   119                         field_desc += ', '
       
   120                     field_desc += ', '.join(['%s=%r' % (k, v) for k, v in extra_params.items()])
       
   121                 field_desc += ')'
       
   122                 if comment_notes:
       
   123                     field_desc += ' # ' + ' '.join(comment_notes)
       
   124                 yield '    %s' % field_desc
       
   125             yield '    class Meta:'
       
   126             yield '        db_table = %r' % table_name
       
   127             yield ''