Author: jbronn
Date: 2010-01-18 15:02:47 -0600 (Mon, 18 Jan 2010)
New Revision: 12257

Added:
   django/trunk/django/contrib/gis/db/backends/mysql/introspection.py
   django/trunk/django/contrib/gis/db/backends/oracle/introspection.py
   django/trunk/django/contrib/gis/db/backends/postgis/introspection.py
   django/trunk/django/contrib/gis/db/backends/spatialite/introspection.py
Modified:
   django/trunk/django/contrib/gis/db/backends/mysql/base.py
   django/trunk/django/contrib/gis/db/backends/oracle/base.py
   django/trunk/django/contrib/gis/db/backends/postgis/base.py
   django/trunk/django/contrib/gis/db/backends/spatialite/base.py
   django/trunk/django/contrib/gis/management/commands/inspectdb.py
   django/trunk/django/core/management/commands/inspectdb.py
Log:
Fixed #12637 -- GeoDjango's `inspectdb` command is now a subclass of Django's, 
and works with all spatial backends (Oracle and SpatiaLite did work before).  
This changeset introduces new introspection modules for all of the spatial 
backends and adds hooks to the original `inspectdb.Command` class to enable 
reuse.


Modified: django/trunk/django/contrib/gis/db/backends/mysql/base.py
===================================================================
--- django/trunk/django/contrib/gis/db/backends/mysql/base.py   2010-01-18 
20:51:29 UTC (rev 12256)
+++ django/trunk/django/contrib/gis/db/backends/mysql/base.py   2010-01-18 
21:02:47 UTC (rev 12257)
@@ -1,6 +1,7 @@
 from django.db.backends.mysql.base import *
 from django.db.backends.mysql.base import DatabaseWrapper as 
MySQLDatabaseWrapper
 from django.contrib.gis.db.backends.mysql.creation import MySQLCreation
+from django.contrib.gis.db.backends.mysql.introspection import 
MySQLIntrospection
 from django.contrib.gis.db.backends.mysql.operations import MySQLOperations
 
 class DatabaseWrapper(MySQLDatabaseWrapper):
@@ -9,3 +10,4 @@
         super(DatabaseWrapper, self).__init__(*args, **kwargs)
         self.creation = MySQLCreation(self)
         self.ops = MySQLOperations()
+        self.introspection = MySQLIntrospection(self)

Added: django/trunk/django/contrib/gis/db/backends/mysql/introspection.py
===================================================================
--- django/trunk/django/contrib/gis/db/backends/mysql/introspection.py          
                (rev 0)
+++ django/trunk/django/contrib/gis/db/backends/mysql/introspection.py  
2010-01-18 21:02:47 UTC (rev 12257)
@@ -0,0 +1,32 @@
+from MySQLdb.constants import FIELD_TYPE
+
+from django.contrib.gis.gdal import OGRGeomType
+from django.db.backends.mysql.introspection import DatabaseIntrospection
+
+class MySQLIntrospection(DatabaseIntrospection):
+    # Updating the data_types_reverse dictionary with the appropriate
+    # type for Geometry fields.
+    data_types_reverse = DatabaseIntrospection.data_types_reverse.copy()
+    data_types_reverse[FIELD_TYPE.GEOMETRY] = 'GeometryField'
+
+    def get_geometry_type(self, table_name, geo_col):
+        cursor = self.connection.cursor()
+        try:
+            # In order to get the specific geometry type of the field,
+            # we introspect on the table definition using `DESCRIBE`.
+            cursor.execute('DESCRIBE %s' %
+                           self.connection.ops.quote_name(table_name))
+            # Increment over description info until we get to the geometry
+            # column.
+            for column, typ, null, key, default, extra in cursor.fetchall():
+                if column == geo_col:
+                    # Using OGRGeomType to convert from OGC name to Django 
field.
+                    # MySQL does not support 3D or SRIDs, so the field params
+                    # are empty.
+                    field_type = OGRGeomType(typ).django
+                    field_params = {}
+                    break
+        finally:
+            cursor.close()
+
+        return field_type, field_params

Modified: django/trunk/django/contrib/gis/db/backends/oracle/base.py
===================================================================
--- django/trunk/django/contrib/gis/db/backends/oracle/base.py  2010-01-18 
20:51:29 UTC (rev 12256)
+++ django/trunk/django/contrib/gis/db/backends/oracle/base.py  2010-01-18 
21:02:47 UTC (rev 12257)
@@ -1,10 +1,12 @@
 from django.db.backends.oracle.base import *
 from django.db.backends.oracle.base import DatabaseWrapper as 
OracleDatabaseWrapper
 from django.contrib.gis.db.backends.oracle.creation import OracleCreation
+from django.contrib.gis.db.backends.oracle.introspection import 
OracleIntrospection
 from django.contrib.gis.db.backends.oracle.operations import OracleOperations
 
 class DatabaseWrapper(OracleDatabaseWrapper):
     def __init__(self, *args, **kwargs):
         super(DatabaseWrapper, self).__init__(*args, **kwargs)
+        self.ops = OracleOperations(self)
         self.creation = OracleCreation(self)
-        self.ops = OracleOperations(self)
+        self.introspection = OracleIntrospection(self)

Added: django/trunk/django/contrib/gis/db/backends/oracle/introspection.py
===================================================================
--- django/trunk/django/contrib/gis/db/backends/oracle/introspection.py         
                (rev 0)
+++ django/trunk/django/contrib/gis/db/backends/oracle/introspection.py 
2010-01-18 21:02:47 UTC (rev 12257)
@@ -0,0 +1,39 @@
+import cx_Oracle
+from django.db.backends.oracle.introspection import DatabaseIntrospection
+
+class OracleIntrospection(DatabaseIntrospection):
+    # Associating any OBJECTVAR instances with GeometryField.  Of course,
+    # this won't work right on Oracle objects that aren't MDSYS.SDO_GEOMETRY,
+    # but it is the only object type supported within Django anyways.
+    data_types_reverse = DatabaseIntrospection.data_types_reverse.copy()
+    data_types_reverse[cx_Oracle.OBJECT] = 'GeometryField'
+
+    def get_geometry_type(self, table_name, geo_col):
+        cursor = self.connection.cursor()
+        try:
+            # Querying USER_SDO_GEOM_METADATA to get the SRID and dimension 
information.
+            try:
+                cursor.execute('SELECT "DIMINFO", "SRID" FROM 
"USER_SDO_GEOM_METADATA" WHERE "TABLE_NAME"=%s AND "COLUMN_NAME"=%s',
+                               (table_name.upper(), geo_col.upper()))
+                row = cursor.fetchone()
+            except Exception, msg:
+                raise Exception('Could not find entry in 
USER_SDO_GEOM_METADATA corresponding to "%s"."%s"\n'
+                                'Error message: %s.' % (table_name, geo_col, 
msg))
+
+            # TODO: Research way to find a more specific geometry field type 
for
+            # the column's contents.
+            field_type = 'GeometryField'
+
+            # Getting the field parameters.
+            field_params = {}
+            dim, srid = row
+            if srid != 4326:
+                field_params['srid'] = srid
+            # Length of object array ( SDO_DIM_ARRAY ) is number of dimensions.
+            dim = len(dim)
+            if dim != 2:
+                field_params['dim'] = dim
+        finally:
+            cursor.close()
+
+        return field_type, field_params

Modified: django/trunk/django/contrib/gis/db/backends/postgis/base.py
===================================================================
--- django/trunk/django/contrib/gis/db/backends/postgis/base.py 2010-01-18 
20:51:29 UTC (rev 12256)
+++ django/trunk/django/contrib/gis/db/backends/postgis/base.py 2010-01-18 
21:02:47 UTC (rev 12257)
@@ -1,6 +1,7 @@
 from django.db.backends.postgresql_psycopg2.base import *
 from django.db.backends.postgresql_psycopg2.base import DatabaseWrapper as 
Psycopg2DatabaseWrapper
 from django.contrib.gis.db.backends.postgis.creation import PostGISCreation
+from django.contrib.gis.db.backends.postgis.introspection import 
PostGISIntrospection
 from django.contrib.gis.db.backends.postgis.operations import PostGISOperations
 
 class DatabaseWrapper(Psycopg2DatabaseWrapper):
@@ -8,3 +9,4 @@
         super(DatabaseWrapper, self).__init__(*args, **kwargs)
         self.creation = PostGISCreation(self)
         self.ops = PostGISOperations(self)
+        self.introspection = PostGISIntrospection(self)

Added: django/trunk/django/contrib/gis/db/backends/postgis/introspection.py
===================================================================
--- django/trunk/django/contrib/gis/db/backends/postgis/introspection.py        
                        (rev 0)
+++ django/trunk/django/contrib/gis/db/backends/postgis/introspection.py        
2010-01-18 21:02:47 UTC (rev 12257)
@@ -0,0 +1,95 @@
+from django.db.backends.postgresql_psycopg2.introspection import 
DatabaseIntrospection
+from django.contrib.gis.gdal import OGRGeomType
+
+class GeoIntrospectionError(Exception):
+    pass
+
+class PostGISIntrospection(DatabaseIntrospection):
+    # Reverse dictionary for PostGIS geometry types not populated until
+    # introspection is actually performed.
+    postgis_types_reverse = {}
+
+    def get_postgis_types(self):
+        """
+        Returns a dictionary with keys that are the PostgreSQL object
+        identification integers for the PostGIS geometry and/or
+        geography types (if supported).
+        """
+        cursor = self.connection.cursor()
+        # The OID integers associated with the geometry type may
+        # be different across versions; hence, this is why we have
+        # to query the PostgreSQL pg_type table corresponding to the
+        # PostGIS custom data types.
+        oid_sql = 'SELECT "oid" FROM "pg_type" WHERE "typname" = %s'
+        try:
+            cursor.execute(oid_sql, ('geometry',))
+            GEOM_TYPE = cursor.fetchone()[0]
+            postgis_types = { GEOM_TYPE : 'GeometryField' }
+            if self.connection.ops.geography:
+                cursor.execute(oid_sql, ('geography',))
+                GEOG_TYPE = cursor.fetchone()[0]
+                # The value for the geography type is actually a tuple
+                # to pass in the `geography=True` keyword to the field
+                # definition.
+                postgis_types[GEOG_TYPE] = ('GeometryField', {'geography' : 
True})
+        finally:
+            cursor.close()
+
+        return postgis_types
+
+    def get_field_type(self, data_type, description):
+        if not self.postgis_types_reverse:
+            # If the PostGIS types reverse dictionary is not populated, do so
+            # now.  In order to prevent unnecessary requests upon connection
+            # intialization, the `data_types_reverse` dictionary is not updated
+            # with the PostGIS custom types until introspection is actually
+            # performed -- in other words, when this function is called.
+            self.postgis_types_reverse = self.get_postgis_types()
+            self.data_types_reverse.update(self.postgis_types_reverse)
+        return super(PostGISIntrospection, self).get_field_type(data_type, 
description)
+
+    def get_geometry_type(self, table_name, geo_col):
+        """
+        The geometry type OID used by PostGIS does not indicate the particular
+        type of field that a geometry column is (e.g., whether it's a
+        PointField or a PolygonField).  Thus, this routine queries the PostGIS
+        metadata tables to determine the geometry type,
+        """
+        cursor = self.connection.cursor()
+        try:
+            try:
+                # First seeing if this geometry column is in the 
`geometry_columns`
+                cursor.execute('SELECT "coord_dimension", "srid", "type" '
+                               'FROM "geometry_columns" '
+                               'WHERE "f_table_name"=%s AND 
"f_geometry_column"=%s',
+                               (table_name, geo_col))
+                row = cursor.fetchone()
+                if not row: raise GeoIntrospectionError
+            except GeoIntrospectionError:
+                if self.connection.ops.geography:
+                    cursor.execute('SELECT "coord_dimension", "srid", "type" '
+                                   'FROM "geography_columns" '
+                                   'WHERE "f_table_name"=%s AND 
"f_geography_column"=%s',
+                                   (table_name, geo_col))
+                    row = cursor.fetchone()
+
+            if not row:
+                raise Exception('Could not find a geometry or geography column 
for "%s"."%s"' %
+                                (table_name, geo_col))
+
+            # OGRGeomType does not require GDAL and makes it easy to convert
+            # from OGC geom type name to Django field.
+            field_type = OGRGeomType(row[2]).django
+
+            # Getting any GeometryField keyword arguments that are not the 
default.
+            dim = row[0]
+            srid = row[1]
+            field_params = {}
+            if srid != 4326:
+                field_params['srid'] = srid
+            if dim != 2:
+                field_params['dim'] = dim
+        finally:
+            cursor.close()
+
+        return field_type, field_params

Modified: django/trunk/django/contrib/gis/db/backends/spatialite/base.py
===================================================================
--- django/trunk/django/contrib/gis/db/backends/spatialite/base.py      
2010-01-18 20:51:29 UTC (rev 12256)
+++ django/trunk/django/contrib/gis/db/backends/spatialite/base.py      
2010-01-18 21:02:47 UTC (rev 12257)
@@ -7,6 +7,7 @@
     _sqlite_extract, _sqlite_date_trunc, _sqlite_regexp
 from django.contrib.gis.db.backends.spatialite.client import SpatiaLiteClient
 from django.contrib.gis.db.backends.spatialite.creation import 
SpatiaLiteCreation
+from django.contrib.gis.db.backends.spatialite.introspection import 
SpatiaLiteIntrospection
 from django.contrib.gis.db.backends.spatialite.operations import 
SpatiaLiteOperations
 
 class DatabaseWrapper(SqliteDatabaseWrapper):
@@ -32,6 +33,7 @@
         self.ops = SpatiaLiteOperations(self)
         self.client = SpatiaLiteClient(self)
         self.creation = SpatiaLiteCreation(self)
+        self.introspection = SpatiaLiteIntrospection(self)
 
     def _cursor(self):
         if self.connection is None:

Added: django/trunk/django/contrib/gis/db/backends/spatialite/introspection.py
===================================================================
--- django/trunk/django/contrib/gis/db/backends/spatialite/introspection.py     
                        (rev 0)
+++ django/trunk/django/contrib/gis/db/backends/spatialite/introspection.py     
2010-01-18 21:02:47 UTC (rev 12257)
@@ -0,0 +1,51 @@
+from django.contrib.gis.gdal import OGRGeomType
+from django.db.backends.sqlite3.introspection import DatabaseIntrospection, 
FlexibleFieldLookupDict
+
+class GeoFlexibleFieldLookupDict(FlexibleFieldLookupDict):
+    """
+    Sublcass that includes updates the `base_data_types_reverse` dict
+    for geometry field types.
+    """
+    base_data_types_reverse = 
FlexibleFieldLookupDict.base_data_types_reverse.copy()
+    base_data_types_reverse.update(
+        {'point' : 'GeometryField',
+         'linestring' : 'GeometryField',
+         'polygon' : 'GeometryField',
+         'multipoint' : 'GeometryField',
+         'multilinestring' : 'GeometryField',
+         'multipolygon' : 'GeometryField',
+         'geometrycollection' : 'GeometryField',
+         })
+
+class SpatiaLiteIntrospection(DatabaseIntrospection):
+    data_types_reverse = GeoFlexibleFieldLookupDict()
+
+    def get_geometry_type(self, table_name, geo_col):
+        cursor = self.connection.cursor()
+        try:
+            # Querying the `geometry_columns` table to get additional metadata.
+            cursor.execute('SELECT "coord_dimension", "srid", "type" '
+                           'FROM "geometry_columns" '
+                           'WHERE "f_table_name"=%s AND 
"f_geometry_column"=%s',
+                           (table_name, geo_col))
+            row = cursor.fetchone()
+            if not row:
+                raise Exception('Could not find a geometry column for 
"%s"."%s"' %
+                                (table_name, geo_col))
+
+            # OGRGeomType does not require GDAL and makes it easy to convert
+            # from OGC geom type name to Django field.
+            field_type = OGRGeomType(row[2]).django
+
+            # Getting any GeometryField keyword arguments that are not the 
default.
+            dim = row[0]
+            srid = row[1]
+            field_params = {}
+            if srid != 4326:
+                field_params['srid'] = srid
+            if isinstance(dim, basestring) and 'Z' in dim:
+                field_params['dim'] = 3
+        finally:
+            cursor.close()
+
+        return field_type, field_params

Modified: django/trunk/django/contrib/gis/management/commands/inspectdb.py
===================================================================
--- django/trunk/django/contrib/gis/management/commands/inspectdb.py    
2010-01-18 20:51:29 UTC (rev 12256)
+++ django/trunk/django/contrib/gis/management/commands/inspectdb.py    
2010-01-18 21:02:47 UTC (rev 12257)
@@ -1,188 +1,32 @@
-"""
- This overrides the traditional `inspectdb` command so that geographic 
databases
- may be introspected.
-"""
+from optparse import make_option
 
-from django.core.management.commands.inspectdb import Command as InspectCommand
-from django.contrib.gis.db.backend import SpatialBackend
+from django.core.management.base import CommandError
+from django.core.management.commands.inspectdb import Command as 
InspectDBCommand
 
-class Command(InspectCommand):
+class Command(InspectDBCommand):
+    db_module = 'django.contrib.gis.db'
+    gis_tables = {}
 
-    # Mapping from lower-case OGC type to the corresponding GeoDjango field.
-    geofield_mapping = {'point' : 'PointField',
-                        'linestring' : 'LineStringField',
-                        'polygon' : 'PolygonField',
-                        'multipoint' : 'MultiPointField',
-                        'multilinestring' : 'MultiLineStringField',
-                        'multipolygon' : 'MultiPolygonField',
-                        'geometrycollection' : 'GeometryCollectionField',
-                        'geometry' : 'GeometryField',
-                        }
-
-    def geometry_columns(self):
-        """
-        Returns a datastructure of metadata information associated with the
-        `geometry_columns` (or equivalent) table.
-        """
-        # The `geo_cols` is a dictionary data structure that holds information
-        # about any geographic columns in the database.
-        geo_cols = {}
-        def add_col(table, column, coldata):
-            if table in geo_cols:
-                # If table already has a geometry column.
-                geo_cols[table][column] = coldata
+    def get_field_type(self, connection, table_name, row):
+        field_type, field_params, field_notes = super(Command, 
self).get_field_type(connection, table_name, row)
+        if field_type == 'GeometryField':
+            geo_col = row[0]
+            # Getting a more specific field type and any additional parameters
+            # from the `get_geometry_type` routine for the spatial backend.
+            field_type, geo_params = 
connection.introspection.get_geometry_type(table_name, geo_col)
+            field_params.update(geo_params)
+            # Adding the table name and column to the `gis_tables` dictionary, 
this
+            # allows us to track which tables need a GeoManager.
+            if table_name in self.gis_tables:
+                self.gis_tables[table_name].append(geo_col)
             else:
-                # Otherwise, create a dictionary indexed by column.
-                geo_cols[table] = { column : coldata }
+                self.gis_tables[table_name] = [geo_col]
+        return field_type, field_params, field_notes
 
-        if SpatialBackend.name == 'postgis':
-            # PostGIS holds all geographic column information in the 
`geometry_columns` table.
-            from django.contrib.gis.models import GeometryColumns
-            for geo_col in GeometryColumns.objects.all():
-                table = geo_col.f_table_name
-                column = geo_col.f_geometry_column
-                coldata = {'type' : geo_col.type, 'srid' : geo_col.srid, 'dim' 
: geo_col.coord_dimension}
-                add_col(table, column, coldata)
-            return geo_cols
-        elif SpatialBackend.name == 'mysql':
-            # On MySQL have to get all table metadata before hand; this means 
walking through
-            # each table and seeing if any column types are spatial.  Can't 
detect this with
-            # `cursor.description` (what the introspection module does) 
because all spatial types
-            # have the same integer type (255 for GEOMETRY).
-            from django.db import connection
-            cursor = connection.cursor()
-            cursor.execute('SHOW TABLES')
-            tables = cursor.fetchall();
-            for table_tup in tables:
-                table = table_tup[0]
-                table_desc = cursor.execute('DESCRIBE `%s`' % table)
-                col_info = cursor.fetchall()
-                for column, typ, null, key, default, extra in col_info:
-                    if typ in self.geofield_mapping: add_col(table, column, 
{'type' : typ})
-            return geo_cols
-        else:
-            # TODO: Oracle (has incomplete `geometry_columns` -- have to parse
-            #  SDO SQL to get specific type, SRID, and other information).
-            raise NotImplementedError('Geographic database inspection not 
available.')
-
-    def handle_inspection(self):
-        "Overloaded from Django's version to handle geographic database 
tables."
-        from django.db import connection
-        import keyword
-
-        geo_cols = self.geometry_columns()
-
-        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.contrib.gis.db import models'
-        yield ''
-        for table_name in connection.introspection.get_table_list(cursor):
-            # Getting the geographic table dictionary.
-            geo_table = geo_cols.get(table_name, {})
-
-            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)):
-                att_name, iatt_name = row[0].lower(), row[0]
-                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:
-                    if iatt_name in geo_table:
-                        ## Customization for Geographic Columns ##
-                        geo_col = geo_table[iatt_name]
-                        field_type = 
self.geofield_mapping[geo_col['type'].lower()]
-                        # Adding extra keyword arguments for the SRID and 
dimension (if not defaults).
-                        dim, srid = geo_col.get('dim', 2), geo_col.get('srid', 
4326)
-                        if dim != 2: extra_params['dim'] = dim
-                        if srid != 4326: extra_params['srid'] = srid
-                    else:
-                        try:
-                            field_type = 
connection.introspection.get_field_type(row[1], row)
-                        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
-            if table_name in geo_cols:
-                yield '    objects = models.GeoManager()'
-            yield '    class Meta:'
-            yield '        db_table = %r' % table_name
-            yield ''
+    def get_meta(self, table_name):
+        meta_lines = super(Command, self).get_meta(table_name)
+        if table_name in self.gis_tables:
+            # If the table is a geographic one, then we need make
+            # GeoManager the default manager for the model.
+            meta_lines.insert(0, '    objects = models.GeoManager()')
+        return meta_lines

Modified: django/trunk/django/core/management/commands/inspectdb.py
===================================================================
--- django/trunk/django/core/management/commands/inspectdb.py   2010-01-18 
20:51:29 UTC (rev 12256)
+++ django/trunk/django/core/management/commands/inspectdb.py   2010-01-18 
21:02:47 UTC (rev 12257)
@@ -15,6 +15,8 @@
 
     requires_model_validation = False
 
+    db_module = 'django.db'
+
     def handle_noargs(self, **options):
         try:
             for line in self.handle_inspection(options):
@@ -37,7 +39,7 @@
         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 '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)
@@ -81,26 +83,12 @@
                     else:
                         extra_params['db_column'] = column_name
                 else:
-                    try:
-                        field_type = 
connection.introspection.get_field_type(row[1], row)
-                    except KeyError:
-                        field_type = 'TextField'
-                        comment_notes.append('This field type is a guess.')
+                    # 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)
 
-                    # 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.
                     if column_name in indexes:
                         if indexes[column_name]['primary_key']:
@@ -131,6 +119,46 @@
                 if comment_notes:
                     field_desc += ' # ' + ' '.join(comment_notes)
                 yield '    %s' % field_desc
-            yield '    class Meta:'
-            yield '        db_table = %r' % table_name
-            yield ''
+            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,
+                '']

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.


Reply via email to