diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 7de52d7592..45fb7c77f0 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -55,6 +55,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): can_release_savepoints = True supports_tablespaces = True supports_transactions = True + can_introspect_autofield = True can_introspect_ip_address_field = True can_introspect_small_integer_field = True can_distinct_on_fields = True diff --git a/django/db/backends/postgresql_psycopg2/introspection.py b/django/db/backends/postgresql_psycopg2/introspection.py index 1b98f89b70..aa9ab85a4d 100644 --- a/django/db/backends/postgresql_psycopg2/introspection.py +++ b/django/db/backends/postgresql_psycopg2/introspection.py @@ -1,9 +1,13 @@ from __future__ import unicode_literals +from collections import namedtuple from django.db.backends import BaseDatabaseIntrospection, FieldInfo, TableInfo from django.utils.encoding import force_text +FieldInfo = namedtuple('FieldInfo', FieldInfo._fields + ('default',)) + + class DatabaseIntrospection(BaseDatabaseIntrospection): # Maps type codes to Django Field types. data_types_reverse = { @@ -28,6 +32,12 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): ignored_tables = [] + def get_field_type(self, data_type, description): + field_type = super(DatabaseIntrospection, self).get_field_type(data_type, description) + if field_type == 'IntegerField' and description.default and 'nextval' in description.default: + return 'AutoField' + return field_type + def get_table_list(self, cursor): """ Returns a list of table and view names in the current database. @@ -48,12 +58,13 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): # As cursor.description does not return reliably the nullable property, # we have to query the information_schema (#7783) cursor.execute(""" - SELECT column_name, is_nullable + SELECT column_name, is_nullable, column_default FROM information_schema.columns WHERE table_name = %s""", [table_name]) - null_map = dict(cursor.fetchall()) + field_map = dict((line[0], line[1:]) for line in cursor.fetchall()) cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)) - return [FieldInfo(*((force_text(line[0]),) + line[1:6] + (null_map[force_text(line[0])] == 'YES',))) + return [FieldInfo(*((force_text(line[0]),) + line[1:6] + + (field_map[force_text(line[0])][0] == 'YES', field_map[force_text(line[0])][1]))) for line in cursor.description] def get_relations(self, cursor, table_name): diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index 97317b8298..41a0be4ecc 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -320,7 +320,9 @@ Management Commands * :djadmin:`runserver` now uses daemon threads for faster reloading. -* :djadmin:`inspectdb` now outputs ``Meta.unique_together``. +* :djadmin:`inspectdb` now outputs ``Meta.unique_together``. It is also able to + introspect :class:`~django.db.models.AutoField` for MySQL and PostgreSQL + databases. * When calling management commands from code through :ref:`call_command ` and passing options, the option name can match the command