mirror of
				https://github.com/django/django.git
				synced 2025-10-26 07:06:08 +00:00 
			
		
		
		
	Improved 'django-admin inspectdb' so that it detects ForeignKey relationships -- PostgreSQL only
git-svn-id: http://code.djangoproject.com/svn/django/trunk@395 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -76,7 +76,7 @@ def main(): | |||||||
|             for line in ACTION_MAPPING[action](param): |             for line in ACTION_MAPPING[action](param): | ||||||
|                 print line |                 print line | ||||||
|         except NotImplementedError: |         except NotImplementedError: | ||||||
|             sys.stderr.write("Error: %r isn't supported for the currently selected database backend." % action) |             sys.stderr.write("Error: %r isn't supported for the currently selected database backend.\n" % action) | ||||||
|             sys.exit(1) |             sys.exit(1) | ||||||
|     elif action in ('startapp', 'startproject'): |     elif action in ('startapp', 'startproject'): | ||||||
|         try: |         try: | ||||||
|   | |||||||
| @@ -38,6 +38,7 @@ get_last_insert_id = dbmod.get_last_insert_id | |||||||
| get_date_extract_sql = dbmod.get_date_extract_sql | get_date_extract_sql = dbmod.get_date_extract_sql | ||||||
| get_date_trunc_sql = dbmod.get_date_trunc_sql | get_date_trunc_sql = dbmod.get_date_trunc_sql | ||||||
| get_table_list = dbmod.get_table_list | get_table_list = dbmod.get_table_list | ||||||
|  | get_relations = dbmod.get_relations | ||||||
| OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING | OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING | ||||||
| DATA_TYPES = dbmod.DATA_TYPES | DATA_TYPES = dbmod.DATA_TYPES | ||||||
| DATA_TYPES_REVERSE = dbmod.DATA_TYPES_REVERSE | DATA_TYPES_REVERSE = dbmod.DATA_TYPES_REVERSE | ||||||
|   | |||||||
| @@ -73,6 +73,9 @@ def get_table_list(cursor): | |||||||
|     cursor.execute("SHOW TABLES") |     cursor.execute("SHOW TABLES") | ||||||
|     return [row[0] for row in cursor.fetchall()] |     return [row[0] for row in cursor.fetchall()] | ||||||
|  |  | ||||||
|  | def get_relations(cursor, table_name): | ||||||
|  |     raise NotImplementedError | ||||||
|  |  | ||||||
| OPERATOR_MAPPING = { | OPERATOR_MAPPING = { | ||||||
|     'exact': '=', |     'exact': '=', | ||||||
|     'iexact': 'LIKE', |     'iexact': 'LIKE', | ||||||
|   | |||||||
| @@ -82,6 +82,27 @@ def get_table_list(cursor): | |||||||
|             AND pg_catalog.pg_table_is_visible(c.oid)""") |             AND pg_catalog.pg_table_is_visible(c.oid)""") | ||||||
|     return [row[0] for row in cursor.fetchall()] |     return [row[0] for row in cursor.fetchall()] | ||||||
|  |  | ||||||
|  | def get_relations(cursor, table_name): | ||||||
|  |     """ | ||||||
|  |     Returns a dictionary of {field_index: (field_index_other_table, other_table)} | ||||||
|  |     representing all relationships to the given table. Indexes are 0-based. | ||||||
|  |     """ | ||||||
|  |     cursor.execute(""" | ||||||
|  |         SELECT con.conkey, con.confkey, c2.relname | ||||||
|  |         FROM pg_constraint con, pg_class c1, pg_class c2 | ||||||
|  |         WHERE c1.oid = con.conrelid | ||||||
|  |             AND c2.oid = con.confrelid | ||||||
|  |             AND c1.relname = %s | ||||||
|  |             AND con.contype = 'f'""", [table_name]) | ||||||
|  |     relations = {} | ||||||
|  |     for row in cursor.fetchall(): | ||||||
|  |         try: | ||||||
|  |             # row[0] and row[1] are like "{2}", so strip the curly braces. | ||||||
|  |             relations[int(row[0][1:-1]) - 1] = (int(row[1][1:-1]) - 1, row[2]) | ||||||
|  |         except ValueError: | ||||||
|  |             continue | ||||||
|  |     return relations | ||||||
|  |  | ||||||
| # Register these custom typecasts, because Django expects dates/times to be | # Register these custom typecasts, because Django expects dates/times to be | ||||||
| # in Python's native (standard-library) datetime/time format, whereas psycopg | # in Python's native (standard-library) datetime/time format, whereas psycopg | ||||||
| # use mx.DateTime by default. | # use mx.DateTime by default. | ||||||
|   | |||||||
| @@ -112,6 +112,9 @@ def _sqlite_date_trunc(lookup_type, dt): | |||||||
| def get_table_list(cursor): | def get_table_list(cursor): | ||||||
|     raise NotImplementedError |     raise NotImplementedError | ||||||
|  |  | ||||||
|  | def get_relations(cursor, table_name): | ||||||
|  |     raise NotImplementedError | ||||||
|  |  | ||||||
| # Operators and fields ######################################################## | # Operators and fields ######################################################## | ||||||
|  |  | ||||||
| OPERATOR_MAPPING = { | OPERATOR_MAPPING = { | ||||||
|   | |||||||
| @@ -434,22 +434,40 @@ def inspectdb(db_name): | |||||||
|     "Generator that introspects the tables in the given database name and returns a Django model, one line at a time." |     "Generator that introspects the tables in the given database name and returns a Django model, one line at a time." | ||||||
|     from django.core import db |     from django.core import db | ||||||
|     from django.conf import settings |     from django.conf import settings | ||||||
|  |  | ||||||
|  |     def table2model(table_name): | ||||||
|  |         object_name = table_name.title().replace('_', '') | ||||||
|  |         return object_name.endswith('s') and object_name[:-1] or object_name | ||||||
|  |  | ||||||
|     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 "# 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 "# Feel free to rename the models, but don't rename db_table values or field names." | ||||||
|  |     yield '' | ||||||
|     yield 'from django.core import meta' |     yield 'from django.core import meta' | ||||||
|     yield '' |     yield '' | ||||||
|     for table_name in db.get_table_list(cursor): |     for table_name in db.get_table_list(cursor): | ||||||
|         object_name = table_name.title().replace('_', '') |         yield 'class %s(meta.Model):' % table2model(table_name) | ||||||
|         object_name = object_name.endswith('s') and object_name[:-1] or object_name |  | ||||||
|         yield 'class %s(meta.Model):' % object_name |  | ||||||
|         yield '    db_table = %r' % table_name |         yield '    db_table = %r' % table_name | ||||||
|         yield '    fields = (' |         yield '    fields = (' | ||||||
|  |         try: | ||||||
|  |             relations = db.get_relations(cursor, table_name) | ||||||
|  |         except NotImplementedError: | ||||||
|  |             relations = {} | ||||||
|         cursor.execute("SELECT * FROM %s LIMIT 1" % table_name) |         cursor.execute("SELECT * FROM %s LIMIT 1" % table_name) | ||||||
|         for row in cursor.description: |         for i, row in enumerate(cursor.description): | ||||||
|             field_type = db.DATA_TYPES_REVERSE[row[1]] |             if relations.has_key(i): | ||||||
|             field_desc = 'meta.%s(%r' % (field_type, row[0]) |                 rel = relations[i] | ||||||
|             if field_type == 'CharField': |                 rel_to = rel[1] == table_name and "'self'" or table2model(rel[1]) | ||||||
|                 field_desc += ', maxlength=%s' % (row[3]) |                 field_desc = 'meta.ForeignKey(%s, name=%r' % (rel_to, row[0]) | ||||||
|  |             else: | ||||||
|  |                 field_type = db.DATA_TYPES_REVERSE[row[1]] | ||||||
|  |                 field_desc = 'meta.%s(%r' % (field_type, row[0]) | ||||||
|  |                 if field_type == 'CharField': | ||||||
|  |                     field_desc += ', maxlength=%s' % (row[3]) | ||||||
|             yield '        %s),' % field_desc |             yield '        %s),' % field_desc | ||||||
|         yield '    )' |         yield '    )' | ||||||
|         yield '' |         yield '' | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user