From 9423c4e4a886b373243811e08a40a031ae484772 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sat, 4 Feb 2006 20:08:30 +0000 Subject: [PATCH] Fixed #1328 -- Improved 'inspectdb' to handle Python reserved words as field names. Also updated docs. git-svn-id: http://code.djangoproject.com/svn/django/trunk@2271 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/db/backends/sqlite3.py | 2 +- django/core/management.py | 46 ++++++++++++++++++++---------- docs/django-admin.txt | 18 ++++++++++++ 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/django/core/db/backends/sqlite3.py b/django/core/db/backends/sqlite3.py index 2726b05dd9..55e7d2af30 100644 --- a/django/core/db/backends/sqlite3.py +++ b/django/core/db/backends/sqlite3.py @@ -214,7 +214,7 @@ class FlexibleFieldLookupDict: import re m = re.search(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$', key) if m: - return ('CharField', {'maxlength': m.group(1)}) + return ('CharField', {'maxlength': int(m.group(1))}) raise KeyError DATA_TYPES_REVERSE = FlexibleFieldLookupDict() diff --git a/django/core/management.py b/django/core/management.py index f53a45e96f..f13d80d767 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -5,6 +5,10 @@ import django import os, re, sys, textwrap from optparse import OptionParser +# For Python 2.3 +if not hasattr(__builtins__, 'set'): + from sets import Set as set + MODULE_TEMPLATE = ''' {%% if perms.%(app)s.%(addperm)s or perms.%(app)s.%(changeperm)s %%} {%% if perms.%(app)s.%(changeperm)s %%}{%% endif %%}%(name)s{%% if perms.%(app)s.%(changeperm)s %%}{%% endif %%} @@ -563,6 +567,8 @@ def inspectdb(db_name): object_name = table_name.title().replace('_', '') return object_name.endswith('s') and object_name[:-1] or object_name + reserved_python_words = set(['and', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while']) + settings.DATABASE_NAME = db_name cursor = db.db.cursor() yield "# This is an auto-generated Django model module." @@ -584,37 +590,47 @@ def inspectdb(db_name): relations = {} for i, row in enumerate(db.get_table_description(cursor, table_name)): column_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 column_name in reserved_python_words: + extra_params['db_column'] = column_name + column_name += '_field' + comment_notes.append('Field renamed because it was a Python reserved word.') + if relations.has_key(i): - rel = relations[i] - rel_to = rel[1] == table_name and "'self'" or table2model(rel[1]) + 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'): - field_desc = '%s = meta.ForeignKey(%s' % (column_name[:-3], rel_to) + column_name = column_name[:-3] else: - field_desc = '%s = meta.ForeignKey(%s, db_column=%r' % (column_name, rel_to, column_name) + extra_params['db_column'] = column_name else: try: field_type = db.DATA_TYPES_REVERSE[row[1]] except KeyError: field_type = 'TextField' - field_type_was_guessed = True - else: - field_type_was_guessed = False + 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, extra_params = field_type - else: - extra_params = {} + field_type, new_params = field_type + extra_params.update(new_params) if field_type == 'CharField' and row[3]: extra_params['maxlength'] = row[3] - field_desc = '%s = meta.%s(' % (column_name, field_type) - field_desc += ', '.join(['%s=%s' % (k, v) for k, v in extra_params.items()]) - field_desc += ')' - if field_type_was_guessed: - field_desc += ' # This is a guess!' + field_type += '(' + + field_desc = '%s = meta.%s' % (column_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 yield ' class META:' yield ' db_table = %r' % table_name diff --git a/docs/django-admin.txt b/docs/django-admin.txt index 750bb58970..36ebe8ba67 100644 --- a/docs/django-admin.txt +++ b/docs/django-admin.txt @@ -92,6 +92,24 @@ Use this if you have a legacy database with which you'd like to use Django. The script will inspect the database and create a model for each table within it. +As you might expect, the created models will have an attribute for every field +in the table. Note that ``inspectdb`` has a few special cases in its field-name +output: + + * If ``inspectdb`` cannot map a column's type to a model field type, it'll + use ``TextField`` and will insert the Python comment + ``'This field type is a guess.'`` next to the field in the generated + model. + + * **New in Django development version.** If the database column name is a + Python reserved word (such as ``'pass'``, ``'class'`` or ``'for'``), + ``inspectdb`` will append ``'_field'`` to the attribute name. For + example, if a table has a column ``'for'``, the generated model will have + a field ``'for_field'``, with the ``db_column`` attribute set to + ``'for'``. ``inspectdb`` will insert the Python comment + ``'Field renamed because it was a Python reserved word.'`` next to the + field. + 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: