From f594bc4445798cfa455a85291e2a8975eeb4f6dc Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sat, 18 Feb 2006 22:14:05 +0000 Subject: [PATCH] magic-removal: Merged to [2350] git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2351 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 1 + django/core/management.py | 35 ++++++++++++++----- django/db/backends/ado_mssql/introspection.py | 3 ++ django/db/backends/dummy/introspection.py | 1 + django/db/backends/mysql/introspection.py | 13 +++++++ .../db/backends/postgresql/introspection.py | 30 ++++++++++++++++ django/db/backends/sqlite3/introspection.py | 3 ++ docs/django-admin.txt | 11 +++--- 8 files changed, 83 insertions(+), 14 deletions(-) diff --git a/AUTHORS b/AUTHORS index b84a51fbed..e5d43deaae 100644 --- a/AUTHORS +++ b/AUTHORS @@ -51,6 +51,7 @@ answer newbie questions, and generally made Django that much better: deric@monowerks.com Jeremy Dunck Clint Ecker + gandalf@owca.info Baishampayan Ghose Espen Grindhaug Gustavo Picon diff --git a/django/core/management.py b/django/core/management.py index 1bbe8d7444..48ea67b8a2 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -752,7 +752,7 @@ def inspectdb(db_name): 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 "# * Add primary_key=True to one field in each model." + 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 sqlinitialdata [appname]'" @@ -766,23 +766,27 @@ def inspectdb(db_name): 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)): - column_name = row[0] + att_name = row[0] comment_notes = [] # Holds Field notes, to be displayed in a Python comment. extra_params = {} # Holds Field parameters such as 'db_column'. - if keyword.iskeyword(column_name): - extra_params['db_column'] = column_name - column_name += '_field' + 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 relations.has_key(i): rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1]) field_type = 'ForeignKey(%s' % rel_to - if column_name.endswith('_id'): - column_name = column_name[:-3] + if att_name.endswith('_id'): + att_name = att_name[:-3] else: - extra_params['db_column'] = column_name + extra_params['db_column'] = att_name else: try: field_type = introspection_module.DATA_TYPES_REVERSE[row[1]] @@ -797,13 +801,26 @@ def inspectdb(db_name): field_type, new_params = field_type extra_params.update(new_params) + # Add maxlength for all CharFields. if field_type == 'CharField' and row[3]: extra_params['maxlength'] = row[3] + # 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 - field_desc = '%s = models.%s' % (column_name, field_type) + field_desc = '%s = models.%s' % (att_name, field_type) if extra_params: if not field_desc.endswith('('): field_desc += ', ' diff --git a/django/db/backends/ado_mssql/introspection.py b/django/db/backends/ado_mssql/introspection.py index a193b59cc1..b125cc995f 100644 --- a/django/db/backends/ado_mssql/introspection.py +++ b/django/db/backends/ado_mssql/introspection.py @@ -7,4 +7,7 @@ def get_table_description(cursor, table_name): def get_relations(cursor, table_name): raise NotImplementedError +def get_indexes(cursor, table_name): + raise NotImplementedError + DATA_TYPES_REVERSE = {} diff --git a/django/db/backends/dummy/introspection.py b/django/db/backends/dummy/introspection.py index 81dc8efdf0..c52a812046 100644 --- a/django/db/backends/dummy/introspection.py +++ b/django/db/backends/dummy/introspection.py @@ -3,5 +3,6 @@ from django.db.backends.dummy.base import complain get_table_list = complain get_table_description = complain get_relations = complain +get_indexes = complain DATA_TYPES_REVERSE = {} diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py index d33b5bbfe1..b4389e6030 100644 --- a/django/db/backends/mysql/introspection.py +++ b/django/db/backends/mysql/introspection.py @@ -14,6 +14,19 @@ def get_table_description(cursor, table_name): def get_relations(cursor, table_name): raise NotImplementedError +def get_indexes(cursor, table_name): + """ + Returns a dictionary of fieldname -> infodict for the given table, + where each infodict is in the format: + {'primary_key': boolean representing whether it's the primary key, + 'unique': boolean representing whether it's a unique index} + """ + cursor.execute("SHOW INDEX FROM %s" % quote_name(table_name)) + indexes = {} + for row in cursor.fetchall(): + indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])} + return indexes + DATA_TYPES_REVERSE = { FIELD_TYPE.BLOB: 'TextField', FIELD_TYPE.CHAR: 'CharField', diff --git a/django/db/backends/postgresql/introspection.py b/django/db/backends/postgresql/introspection.py index c06b523211..63893bd15b 100644 --- a/django/db/backends/postgresql/introspection.py +++ b/django/db/backends/postgresql/introspection.py @@ -37,6 +37,36 @@ def get_relations(cursor, table_name): continue return relations +def get_indexes(cursor, table_name): + """ + Returns a dictionary of fieldname -> infodict for the given table, + where each infodict is in the format: + {'primary_key': boolean representing whether it's the primary key, + 'unique': boolean representing whether it's a unique index} + """ + # Get the table description because we only have the column indexes, and we + # need the column names. + desc = get_table_description(cursor, table_name) + # This query retrieves each index on the given table. + cursor.execute(""" + SELECT idx.indkey, idx.indisunique, idx.indisprimary + FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, + pg_catalog.pg_index idx + WHERE c.oid = idx.indrelid + AND idx.indexrelid = c2.oid + AND c.relname = %s""", [table_name]) + indexes = {} + for row in cursor.fetchall(): + # row[0] (idx.indkey) is stored in the DB as an array. It comes out as + # a string of space-separated integers. This designates the field + # indexes (1-based) of the fields that have indexes on the table. + # Here, we skip any indexes across multiple fields. + if ' ' in row[0]: + continue + col_name = desc[int(row[0])-1][0] + indexes[col_name] = {'primary_key': row[2], 'unique': row[1]} + return indexes + # Maps type codes to Django Field types. DATA_TYPES_REVERSE = { 16: 'BooleanField', diff --git a/django/db/backends/sqlite3/introspection.py b/django/db/backends/sqlite3/introspection.py index 26b7cc0695..0ca971c0d4 100644 --- a/django/db/backends/sqlite3/introspection.py +++ b/django/db/backends/sqlite3/introspection.py @@ -11,6 +11,9 @@ def get_table_description(cursor, table_name): def get_relations(cursor, table_name): raise NotImplementedError +def get_indexes(cursor, table_name): + raise NotImplementedError + # Maps SQL types to Django Field types. Some of the SQL types have multiple # entries here because SQLite allows for anything and doesn't normalize the # field type; it uses whatever was given. diff --git a/docs/django-admin.txt b/docs/django-admin.txt index 36ebe8ba67..45cc2363dc 100644 --- a/docs/django-admin.txt +++ b/docs/django-admin.txt @@ -112,12 +112,13 @@ output: This feature is meant as a shortcut, not as definitive model generation. After you run it, you'll want to look over the generated models yourself to make -customizations. In particular, you'll need to do this: +customizations. In particular, you'll need to rearrange models' order, so that +models that refer to other models are ordered properly. - * Rearrange models' order, so that models that refer to other models are - ordered properly. - * Add ``primary_key=True`` to one field in each model. The ``inspectdb`` - doesn't yet introspect primary keys. +If you're using Django 0.90 or 0.91, you'll need to add ``primary_key=True`` to +one field in each model. In the Django development version, primary keys are +automatically introspected for PostgreSQL and MySQL, and Django puts in the +``primary_key=True`` where needed. ``inspectdb`` works with PostgreSQL, MySQL and SQLite. Foreign-key detection only works in PostgreSQL.