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
This commit is contained in:
Adrian Holovaty 2006-02-04 20:08:30 +00:00
parent ee484f1c48
commit 9423c4e4a8
3 changed files with 50 additions and 16 deletions

View File

@ -214,7 +214,7 @@ class FlexibleFieldLookupDict:
import re import re
m = re.search(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$', key) m = re.search(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$', key)
if m: if m:
return ('CharField', {'maxlength': m.group(1)}) return ('CharField', {'maxlength': int(m.group(1))})
raise KeyError raise KeyError
DATA_TYPES_REVERSE = FlexibleFieldLookupDict() DATA_TYPES_REVERSE = FlexibleFieldLookupDict()

View File

@ -5,6 +5,10 @@ import django
import os, re, sys, textwrap import os, re, sys, textwrap
from optparse import OptionParser 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 %%} MODULE_TEMPLATE = ''' {%% if perms.%(app)s.%(addperm)s or perms.%(app)s.%(changeperm)s %%}
<tr> <tr>
<th>{%% if perms.%(app)s.%(changeperm)s %%}<a href="%(app)s/%(mod)s/">{%% endif %%}%(name)s{%% if perms.%(app)s.%(changeperm)s %%}</a>{%% endif %%}</th> <th>{%% if perms.%(app)s.%(changeperm)s %%}<a href="%(app)s/%(mod)s/">{%% endif %%}%(name)s{%% if perms.%(app)s.%(changeperm)s %%}</a>{%% endif %%}</th>
@ -563,6 +567,8 @@ def inspectdb(db_name):
object_name = table_name.title().replace('_', '') object_name = table_name.title().replace('_', '')
return object_name.endswith('s') and object_name[:-1] or object_name 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 settings.DATABASE_NAME = db_name
cursor = db.db.cursor() cursor = db.db.cursor()
yield "# This is an auto-generated Django model module." yield "# This is an auto-generated Django model module."
@ -584,37 +590,47 @@ def inspectdb(db_name):
relations = {} relations = {}
for i, row in enumerate(db.get_table_description(cursor, table_name)): for i, row in enumerate(db.get_table_description(cursor, table_name)):
column_name = row[0] 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): if relations.has_key(i):
rel = relations[i] rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1])
rel_to = rel[1] == table_name and "'self'" or table2model(rel[1]) field_type = 'ForeignKey(%s' % rel_to
if column_name.endswith('_id'): if column_name.endswith('_id'):
field_desc = '%s = meta.ForeignKey(%s' % (column_name[:-3], rel_to) column_name = column_name[:-3]
else: else:
field_desc = '%s = meta.ForeignKey(%s, db_column=%r' % (column_name, rel_to, column_name) extra_params['db_column'] = column_name
else: else:
try: try:
field_type = db.DATA_TYPES_REVERSE[row[1]] field_type = db.DATA_TYPES_REVERSE[row[1]]
except KeyError: except KeyError:
field_type = 'TextField' field_type = 'TextField'
field_type_was_guessed = True comment_notes.append('This field type is a guess.')
else:
field_type_was_guessed = False
# This is a hook for DATA_TYPES_REVERSE to return a tuple of # This is a hook for DATA_TYPES_REVERSE to return a tuple of
# (field_type, extra_params_dict). # (field_type, extra_params_dict).
if type(field_type) is tuple: if type(field_type) is tuple:
field_type, extra_params = field_type field_type, new_params = field_type
else: extra_params.update(new_params)
extra_params = {}
if field_type == 'CharField' and row[3]: if field_type == 'CharField' and row[3]:
extra_params['maxlength'] = row[3] extra_params['maxlength'] = row[3]
field_desc = '%s = meta.%s(' % (column_name, field_type) field_type += '('
field_desc += ', '.join(['%s=%s' % (k, v) for k, v in extra_params.items()])
field_desc += ')' field_desc = '%s = meta.%s' % (column_name, field_type)
if field_type_was_guessed: if extra_params:
field_desc += ' # This is a guess!' 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 ' %s' % field_desc
yield ' class META:' yield ' class META:'
yield ' db_table = %r' % table_name yield ' db_table = %r' % table_name

View File

@ -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 The script will inspect the database and create a model for each table within
it. 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 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 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 do this: