Django

Code

Changeset 384

Show
Ignore:
Timestamp:
08/02/05 12:08:24 (3 years ago)
Author:
adrian
Message:

Added first stab at 'django-admin.py inspectdb', which takes a database name and introspects the tables, outputting a Django model. Works in PostgreSQL and MySQL. It's missing some niceties at the moment, such as detection of primary-keys and relationships, but it works. Refs #90.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/bin/django-admin.py

    r261 r384  
    88    'createsuperuser': management.createsuperuser, 
    99#     'dbcheck': management.database_check, 
     10    'inspectdb': management.inspectdb, 
    1011    'runserver': management.runserver, 
    1112    'sql': management.get_sql_create, 
     
    6768    if action in ('createsuperuser', 'init'): 
    6869        ACTION_MAPPING[action]() 
     70    elif action == 'inspectdb': 
     71        try: 
     72            param = args[1] 
     73        except IndexError: 
     74            parser.print_usage_and_exit() 
     75        try: 
     76            for line in ACTION_MAPPING[action](param): 
     77                print line 
     78        except NotImplementedError: 
     79            sys.stderr.write("Error: %r isn't supported for the currently selected database backend." % action) 
     80            sys.exit(1) 
    6981    elif action in ('startapp', 'startproject'): 
    7082        try: 
  • django/trunk/django/core/db/backends/mysql.py

    r276 r384  
    6969    return "(%s - %s)" % (field_name, ''.join(subtractions)) 
    7070 
     71def get_table_list(cursor): 
     72    "Returns a list of table names in the current database." 
     73    cursor.execute("SHOW TABLES") 
     74    return [row[0] for row in cursor.fetchall()] 
     75 
    7176OPERATOR_MAPPING = { 
    7277    'exact': '=', 
     
    116121    'XMLField':          'text', 
    117122} 
     123 
     124DATA_TYPES_REVERSE = { 
     125    FIELD_TYPE.BLOB: 'TextField', 
     126    FIELD_TYPE.CHAR: 'CharField', 
     127    FIELD_TYPE.DECIMAL: 'FloatField', 
     128    FIELD_TYPE.DATE: 'DateField', 
     129    FIELD_TYPE.DATETIME: 'DateTimeField', 
     130    FIELD_TYPE.DOUBLE: 'FloatField', 
     131    FIELD_TYPE.FLOAT: 'FloatField', 
     132    FIELD_TYPE.INT24: 'IntegerField', 
     133    FIELD_TYPE.LONG: 'IntegerField', 
     134    FIELD_TYPE.LONGLONG: 'IntegerField', 
     135    FIELD_TYPE.SHORT: 'IntegerField', 
     136    FIELD_TYPE.STRING: 'TextField', 
     137    FIELD_TYPE.TIMESTAMP: 'DateTimeField', 
     138    FIELD_TYPE.TINY_BLOB: 'TextField', 
     139    FIELD_TYPE.MEDIUM_BLOB: 'TextField', 
     140    FIELD_TYPE.LONG_BLOB: 'TextField', 
     141    FIELD_TYPE.VAR_STRING: 'CharField', 
     142} 
  • django/trunk/django/core/db/backends/postgresql.py

    r207 r384  
    7272    return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) 
    7373 
     74def get_table_list(cursor): 
     75    "Returns a list of table names in the current database." 
     76    cursor.execute(""" 
     77        SELECT c.relname 
     78        FROM pg_catalog.pg_class c 
     79        LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace 
     80        WHERE c.relkind IN ('r', 'v', '') 
     81            AND n.nspname NOT IN ('pg_catalog', 'pg_toast') 
     82            AND pg_catalog.pg_table_is_visible(c.oid)""") 
     83    return [row[0] for row in cursor.fetchall()] 
     84 
    7485# Register these custom typecasts, because Django expects dates/times to be 
    7586# in Python's native (standard-library) datetime/time format, whereas psycopg 
     
    130141    'XMLField':          'text', 
    131142} 
     143 
     144# Maps type codes to Django Field types. 
     145DATA_TYPES_REVERSE = { 
     146    16: 'BooleanField', 
     147    21: 'SmallIntegerField', 
     148    23: 'IntegerField', 
     149    25: 'TextField', 
     150    869: 'IPAddressField', 
     151    1043: 'CharField', 
     152    1082: 'DateField', 
     153    1083: 'TimeField', 
     154    1114: 'DateTimeField', 
     155    1184: 'DateTimeField', 
     156    1266: 'TimeField', 
     157    1700: 'FloatField', 
     158} 
  • django/trunk/django/core/db/backends/sqlite3.py

    r383 r384  
    6262    you'll need to use "%%s" (which I belive is true of other wrappers as well). 
    6363    """ 
    64      
     64 
    6565    def execute(self, query, params=[]): 
    6666        query = self.convert_query(query, len(params)) 
    6767        return Database.Cursor.execute(self, query, params) 
    68          
     68 
    6969    def executemany(self, query, params=[]): 
    7070        query = self.convert_query(query, len(params[0])) 
    7171        return Database.Cursor.executemany(self, query, params) 
    72          
     72 
    7373    def convert_query(self, query, num_params): 
    7474        # XXX this seems too simple to be correct... is this right? 
     
    7979def get_last_insert_id(cursor, table_name, pk_name): 
    8080    return cursor.lastrowid 
    81      
     81 
    8282def get_date_extract_sql(lookup_type, table_name): 
    8383    # lookup_type is 'year', 'month', 'day' 
    84     # sqlite doesn't support extract, so we fake it with the user-defined  
     84    # sqlite doesn't support extract, so we fake it with the user-defined 
    8585    # function _sqlite_extract that's registered in connect(), above. 
    8686    return 'django_extract("%s", %s)' % (lookup_type.lower(), table_name) 
     
    110110        return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day) 
    111111 
     112def get_table_list(cursor): 
     113    raise NotImplementedError 
     114 
    112115# Operators and fields ######################################################## 
    113          
     116 
    114117OPERATOR_MAPPING = { 
    115118    'exact':        '=', 
     
    128131} 
    129132 
    130 # SQLite doesn't actually support most of these types, but it "does the right  
     133# SQLite doesn't actually support most of these types, but it "does the right 
    131134# thing" given more verbose field definitions, so leave them as is so that 
    132135# schema inspection is more useful. 
     
    158161    'XMLField':                     'text', 
    159162} 
     163 
     164DATA_TYPES_REVERSE = {} 
  • django/trunk/django/core/db/__init__.py

    r272 r384  
    3838get_date_extract_sql = dbmod.get_date_extract_sql 
    3939get_date_trunc_sql = dbmod.get_date_trunc_sql 
     40get_table_list = dbmod.get_table_list 
    4041OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING 
    4142DATA_TYPES = dbmod.DATA_TYPES 
     43DATA_TYPES_REVERSE = dbmod.DATA_TYPES_REVERSE 
  • django/trunk/django/core/management.py

    r378 r384  
    431431createsuperuser.args = '' 
    432432 
     433def inspectdb(db_name): 
     434    "Generator that introspects the tables in the given database name and returns a Django model, one line at a time." 
     435    from django.core import db 
     436    from django.conf import settings 
     437    settings.DATABASE_NAME = db_name 
     438    cursor = db.db.cursor() 
     439    yield 'from django.core import meta' 
     440    yield '' 
     441    for table_name in db.get_table_list(cursor): 
     442        object_name = table_name.title().replace('_', '') 
     443        object_name = object_name.endswith('s') and object_name[:-1] or object_name 
     444        yield 'class %s(meta.Model):' % object_name 
     445        yield '    db_table = %r' % table_name 
     446        yield '    fields = (' 
     447        cursor.execute("SELECT * FROM %s LIMIT 1" % table_name) 
     448        for row in cursor.description: 
     449            field_type = db.DATA_TYPES_REVERSE[row[1]] 
     450            field_desc = 'meta.%s(%r' % (field_type, row[0]) 
     451            if field_type == 'CharField': 
     452                field_desc += ', maxlength=%s' % (row[3]) 
     453            yield '        %s),' % field_desc 
     454        yield '    )' 
     455        yield '' 
     456inspectdb.help_doc = "Introspects the database tables in the given database and outputs a Django model module." 
     457inspectdb.args = "[dbname]" 
     458 
    433459def runserver(port): 
    434460    "Starts a lightweight Web server for development."