From ac97cf54af32a1bf4426e613909f0a9af3e02c94 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sat, 18 Feb 2006 21:26:28 +0000 Subject: [PATCH] Fixed #1286 -- Improved 'inspectdb' so that it introspects primary_key=True and unique=True for MySQL. Thanks, gandalf@owca.info git-svn-id: http://code.djangoproject.com/svn/django/trunk@2346 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 1 + django/core/db/__init__.py | 1 + django/core/db/backends/ado_mssql.py | 3 +++ django/core/db/backends/mysql.py | 8 +++++++ django/core/db/backends/postgresql.py | 3 +++ django/core/db/backends/sqlite3.py | 3 +++ django/core/management.py | 34 ++++++++++++++++++++------- docs/django-admin.txt | 11 +++++---- 8 files changed, 51 insertions(+), 13 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1e5209319b..161ce9b97b 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/db/__init__.py b/django/core/db/__init__.py index 5883acefdd..1d2f04b367 100644 --- a/django/core/db/__init__.py +++ b/django/core/db/__init__.py @@ -37,6 +37,7 @@ get_random_function_sql = dbmod.get_random_function_sql get_table_list = dbmod.get_table_list get_table_description = dbmod.get_table_description get_relations = dbmod.get_relations +get_indexes = dbmod.get_indexes OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING DATA_TYPES = dbmod.DATA_TYPES DATA_TYPES_REVERSE = dbmod.DATA_TYPES_REVERSE diff --git a/django/core/db/backends/ado_mssql.py b/django/core/db/backends/ado_mssql.py index 80f174a7b2..f13cc8b9e8 100644 --- a/django/core/db/backends/ado_mssql.py +++ b/django/core/db/backends/ado_mssql.py @@ -118,6 +118,9 @@ def get_table_description(cursor, table_name): def get_relations(cursor, table_name): raise NotImplementedError +def get_indexes(cursor, table_name): + raise NotImplementedError + OPERATOR_MAPPING = { 'exact': '= %s', 'iexact': 'LIKE %s', diff --git a/django/core/db/backends/mysql.py b/django/core/db/backends/mysql.py index 66376cdd1e..4f80dd0d97 100644 --- a/django/core/db/backends/mysql.py +++ b/django/core/db/backends/mysql.py @@ -135,6 +135,14 @@ def get_table_description(cursor, table_name): def get_relations(cursor, table_name): raise NotImplementedError +def get_indexes(cursor, table_name): + "Returns a dict of indexes for given table" + cursor.execute("SHOW INDEX FROM %s" % DatabaseWrapper().quote_name(table_name)) + indexes = {} + for row in cursor.fetchall(): + indexes[row[4]] = {'keyname': row[2], 'unique': not bool(row[1])} + return indexes + OPERATOR_MAPPING = { 'exact': '= %s', 'iexact': 'LIKE %s', diff --git a/django/core/db/backends/postgresql.py b/django/core/db/backends/postgresql.py index dcbd719bdc..e4094f31f5 100644 --- a/django/core/db/backends/postgresql.py +++ b/django/core/db/backends/postgresql.py @@ -126,6 +126,9 @@ def get_relations(cursor, table_name): continue return relations +def get_indexes(cursor, table_name): + raise NotImplementedError + # Register these custom typecasts, because Django expects dates/times to be # in Python's native (standard-library) datetime/time format, whereas psycopg # use mx.DateTime by default. diff --git a/django/core/db/backends/sqlite3.py b/django/core/db/backends/sqlite3.py index b8a661cf2b..dc477da13d 100644 --- a/django/core/db/backends/sqlite3.py +++ b/django/core/db/backends/sqlite3.py @@ -134,6 +134,9 @@ def get_table_description(cursor, table_name): def get_relations(cursor, table_name): raise NotImplementedError +def get_indexes(cursor, table_name): + raise NotImplementedError + # Operators and fields ######################################################## # SQLite requires LIKE statements to include an ESCAPE clause if the value diff --git a/django/core/management.py b/django/core/management.py index 46938b7642..23ee81d0ad 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -595,23 +595,27 @@ def inspectdb(db_name): relations = db.get_relations(cursor, table_name) except NotImplementedError: relations = {} + try: + indexes = db.get_indexes(cursor, table_name) + except NotImplementedError: + indexes = {} for i, row in enumerate(db.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 = db.DATA_TYPES_REVERSE[row[1]] @@ -625,12 +629,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]['keyname'] == 'PRIMARY': + extra_params['primary_key'] = True + elif indexes[column_name]['unique']: + extra_params['unique'] = True + field_type += '(' - field_desc = '%s = meta.%s' % (column_name, 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 = meta.%s' % (att_name, field_type) if extra_params: if not field_desc.endswith('('): field_desc += ', ' diff --git a/docs/django-admin.txt b/docs/django-admin.txt index 36ebe8ba67..9600a72acf 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 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.