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.