From ccc0e122d42dad0f645407c469383d85bae92048 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Sat, 11 Feb 2012 20:05:50 +0000 Subject: [PATCH] Fixed #7783 -- Made introspection of nullable columns more robust with Postgres. Thanks bthomas AT ncorcle DOT com for the report and initial patch, and Claude Paroz for the final, complete patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@17508 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- .../db/backends/postgresql_psycopg2/introspection.py | 10 +++++++++- tests/regressiontests/introspection/models.py | 2 +- tests/regressiontests/introspection/tests.py | 8 ++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/django/db/backends/postgresql_psycopg2/introspection.py b/django/db/backends/postgresql_psycopg2/introspection.py index 2ce579a9cf..0027ec5eaf 100644 --- a/django/db/backends/postgresql_psycopg2/introspection.py +++ b/django/db/backends/postgresql_psycopg2/introspection.py @@ -34,8 +34,16 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): def get_table_description(self, cursor, table_name): "Returns a description of the table, with the DB-API cursor.description interface." + # 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 + FROM information_schema.columns + WHERE table_name = %s""", [table_name]) + null_map = dict(cursor.fetchall()) cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)) - return cursor.description + return [tuple([item for item in line[:6]] + [null_map[line[0]]==u'YES']) + for line in cursor.description] def get_relations(self, cursor, table_name): """ diff --git a/tests/regressiontests/introspection/models.py b/tests/regressiontests/introspection/models.py index da12f6e5cb..3b8383e601 100644 --- a/tests/regressiontests/introspection/models.py +++ b/tests/regressiontests/introspection/models.py @@ -5,7 +5,7 @@ class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) email = models.EmailField() - facebook_user_id = models.BigIntegerField() + facebook_user_id = models.BigIntegerField(null=True) def __unicode__(self): return u"%s %s" % (self.first_name, self.last_name) diff --git a/tests/regressiontests/introspection/tests.py b/tests/regressiontests/introspection/tests.py index 1835064fcd..fa2b6c5d73 100644 --- a/tests/regressiontests/introspection/tests.py +++ b/tests/regressiontests/introspection/tests.py @@ -78,6 +78,14 @@ class IntrospectionTests(TestCase): ['IntegerField', 'CharField', 'CharField', 'CharField', 'BigIntegerField'] ) + def test_get_table_description_nullable(self): + cursor = connection.cursor() + desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table) + self.assertEqual( + [r[6] for r in desc], + [False, False, False, False, True] + ) + # Regression test for #9991 - 'real' types in postgres @skipUnlessDBFeature('has_real_datatype') def test_postgresql_real_type(self):