summaryrefslogtreecommitdiff
path: root/django/db/backends/postgresql
diff options
context:
space:
mode:
authorRussell Keith-Magee <russell@keith-magee.com>2008-08-11 12:11:25 +0000
committerRussell Keith-Magee <russell@keith-magee.com>2008-08-11 12:11:25 +0000
commit9dc4ba875f21d5690f6ad5995123a67a3c44bafe (patch)
tree621f876758ac16dceee95faf51973d4b05f1c830 /django/db/backends/postgresql
parentcec69eb70d1e2f84dc5a7fb172da88a79b0f5063 (diff)
Fixed #5461 -- Refactored the database backend code to use classes for the creation and introspection modules. Introduces a new validation module for DB-specific validation. This is a backwards incompatible change; see the wiki for details.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@8296 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/db/backends/postgresql')
-rw-r--r--django/db/backends/postgresql/base.py23
-rw-r--r--django/db/backends/postgresql/client.py26
-rw-r--r--django/db/backends/postgresql/creation.py66
-rw-r--r--django/db/backends/postgresql/introspection.py162
4 files changed, 149 insertions, 128 deletions
diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py
index 1dfe34aceb..4a8d6ebef0 100644
--- a/django/db/backends/postgresql/base.py
+++ b/django/db/backends/postgresql/base.py
@@ -4,9 +4,13 @@ PostgreSQL database backend for Django.
Requires psycopg 1: http://initd.org/projects/psycopg1
"""
-from django.utils.encoding import smart_str, smart_unicode
-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, util
+from django.db.backends import *
+from django.db.backends.postgresql.client import DatabaseClient
+from django.db.backends.postgresql.creation import DatabaseCreation
+from django.db.backends.postgresql.introspection import DatabaseIntrospection
from django.db.backends.postgresql.operations import DatabaseOperations
+from django.utils.encoding import smart_str, smart_unicode
+
try:
import psycopg as Database
except ImportError, e:
@@ -59,12 +63,7 @@ class UnicodeCursorWrapper(object):
def __iter__(self):
return iter(self.cursor)
-class DatabaseFeatures(BaseDatabaseFeatures):
- pass # This backend uses all the defaults.
-
class DatabaseWrapper(BaseDatabaseWrapper):
- features = DatabaseFeatures()
- ops = DatabaseOperations()
operators = {
'exact': '= %s',
'iexact': 'ILIKE %s',
@@ -82,6 +81,16 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'iendswith': 'ILIKE %s',
}
+ def __init__(self, *args, **kwargs):
+ super(DatabaseWrapper, self).__init__(*args, **kwargs)
+
+ self.features = BaseDatabaseFeatures()
+ self.ops = DatabaseOperations()
+ self.client = DatabaseClient()
+ self.creation = DatabaseCreation(self)
+ self.introspection = DatabaseIntrospection(self)
+ self.validation = BaseDatabaseValidation()
+
def _cursor(self, settings):
set_tz = False
if self.connection is None:
diff --git a/django/db/backends/postgresql/client.py b/django/db/backends/postgresql/client.py
index 8123ec7848..28daed833a 100644
--- a/django/db/backends/postgresql/client.py
+++ b/django/db/backends/postgresql/client.py
@@ -1,15 +1,17 @@
+from django.db.backends import BaseDatabaseClient
from django.conf import settings
import os
-def runshell():
- args = ['psql']
- if settings.DATABASE_USER:
- args += ["-U", settings.DATABASE_USER]
- if settings.DATABASE_PASSWORD:
- args += ["-W"]
- if settings.DATABASE_HOST:
- args.extend(["-h", settings.DATABASE_HOST])
- if settings.DATABASE_PORT:
- args.extend(["-p", str(settings.DATABASE_PORT)])
- args += [settings.DATABASE_NAME]
- os.execvp('psql', args)
+class DatabaseClient(BaseDatabaseClient):
+ def runshell(self):
+ args = ['psql']
+ if settings.DATABASE_USER:
+ args += ["-U", settings.DATABASE_USER]
+ if settings.DATABASE_PASSWORD:
+ args += ["-W"]
+ if settings.DATABASE_HOST:
+ args.extend(["-h", settings.DATABASE_HOST])
+ if settings.DATABASE_PORT:
+ args.extend(["-p", str(settings.DATABASE_PORT)])
+ args += [settings.DATABASE_NAME]
+ os.execvp('psql', args)
diff --git a/django/db/backends/postgresql/creation.py b/django/db/backends/postgresql/creation.py
index a8877a7d9b..3e537e345e 100644
--- a/django/db/backends/postgresql/creation.py
+++ b/django/db/backends/postgresql/creation.py
@@ -1,28 +1,38 @@
-# This dictionary maps Field objects to their associated PostgreSQL column
-# types, as strings. Column-type strings can contain format strings; they'll
-# be interpolated against the values of Field.__dict__ before being output.
-# If a column type is set to None, it won't be included in the output.
-DATA_TYPES = {
- 'AutoField': 'serial',
- 'BooleanField': 'boolean',
- 'CharField': 'varchar(%(max_length)s)',
- 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
- 'DateField': 'date',
- 'DateTimeField': 'timestamp with time zone',
- 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
- 'FileField': 'varchar(%(max_length)s)',
- 'FilePathField': 'varchar(%(max_length)s)',
- 'FloatField': 'double precision',
- 'IntegerField': 'integer',
- 'IPAddressField': 'inet',
- 'NullBooleanField': 'boolean',
- 'OneToOneField': 'integer',
- 'PhoneNumberField': 'varchar(20)',
- 'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
- 'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)',
- 'SlugField': 'varchar(%(max_length)s)',
- 'SmallIntegerField': 'smallint',
- 'TextField': 'text',
- 'TimeField': 'time',
- 'USStateField': 'varchar(2)',
-}
+from django.conf import settings
+from django.db.backends.creation import BaseDatabaseCreation
+
+class DatabaseCreation(BaseDatabaseCreation):
+ # This dictionary maps Field objects to their associated PostgreSQL column
+ # types, as strings. Column-type strings can contain format strings; they'll
+ # be interpolated against the values of Field.__dict__ before being output.
+ # If a column type is set to None, it won't be included in the output.
+ data_types = {
+ 'AutoField': 'serial',
+ 'BooleanField': 'boolean',
+ 'CharField': 'varchar(%(max_length)s)',
+ 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
+ 'DateField': 'date',
+ 'DateTimeField': 'timestamp with time zone',
+ 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
+ 'FileField': 'varchar(%(max_length)s)',
+ 'FilePathField': 'varchar(%(max_length)s)',
+ 'FloatField': 'double precision',
+ 'IntegerField': 'integer',
+ 'IPAddressField': 'inet',
+ 'NullBooleanField': 'boolean',
+ 'OneToOneField': 'integer',
+ 'PhoneNumberField': 'varchar(20)',
+ 'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
+ 'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)',
+ 'SlugField': 'varchar(%(max_length)s)',
+ 'SmallIntegerField': 'smallint',
+ 'TextField': 'text',
+ 'TimeField': 'time',
+ 'USStateField': 'varchar(2)',
+ }
+
+ def sql_table_creation_suffix(self):
+ assert settings.TEST_DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time."
+ if settings.TEST_DATABASE_CHARSET:
+ return "WITH ENCODING '%s'" % settings.TEST_DATABASE_CHARSET
+ return ''
diff --git a/django/db/backends/postgresql/introspection.py b/django/db/backends/postgresql/introspection.py
index 982c004569..7b3ab3bb8a 100644
--- a/django/db/backends/postgresql/introspection.py
+++ b/django/db/backends/postgresql/introspection.py
@@ -1,86 +1,86 @@
-from django.db.backends.postgresql.base import DatabaseOperations
+from django.db.backends import BaseDatabaseIntrospection
-quote_name = DatabaseOperations().quote_name
+class DatabaseIntrospection(BaseDatabaseIntrospection):
+ # Maps type codes to Django Field types.
+ data_types_reverse = {
+ 16: 'BooleanField',
+ 21: 'SmallIntegerField',
+ 23: 'IntegerField',
+ 25: 'TextField',
+ 701: 'FloatField',
+ 869: 'IPAddressField',
+ 1043: 'CharField',
+ 1082: 'DateField',
+ 1083: 'TimeField',
+ 1114: 'DateTimeField',
+ 1184: 'DateTimeField',
+ 1266: 'TimeField',
+ 1700: 'DecimalField',
+ }
+
+ def get_table_list(self, cursor):
+ "Returns a list of table names in the current database."
+ cursor.execute("""
+ SELECT c.relname
+ FROM pg_catalog.pg_class c
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind IN ('r', 'v', '')
+ AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
+ AND pg_catalog.pg_table_is_visible(c.oid)""")
+ return [row[0] for row in cursor.fetchall()]
-def get_table_list(cursor):
- "Returns a list of table names in the current database."
- cursor.execute("""
- SELECT c.relname
- FROM pg_catalog.pg_class c
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind IN ('r', 'v', '')
- AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
- AND pg_catalog.pg_table_is_visible(c.oid)""")
- return [row[0] for row in cursor.fetchall()]
+ def get_table_description(self, cursor, table_name):
+ "Returns a description of the table, with the DB-API cursor.description interface."
+ cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
+ return cursor.description
-def get_table_description(cursor, table_name):
- "Returns a description of the table, with the DB-API cursor.description interface."
- cursor.execute("SELECT * FROM %s LIMIT 1" % quote_name(table_name))
- return cursor.description
+ def get_relations(self, 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
-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
+ def get_indexes(self, cursor, table_name):
+ """
+ Returns a dictionary of fieldname -> infodict for the given table,
+ where each infodict is in the format:
+ {'primary_key': boolean representing whether it's the primary key,
+ 'unique': boolean representing whether it's a unique index}
+ """
+ # This query retrieves each index on the given table, including the
+ # first associated field name
+ cursor.execute("""
+ SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
+ FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
+ pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
+ WHERE c.oid = idx.indrelid
+ AND idx.indexrelid = c2.oid
+ AND attr.attrelid = c.oid
+ AND attr.attnum = idx.indkey[0]
+ AND c.relname = %s""", [table_name])
+ indexes = {}
+ for row in cursor.fetchall():
+ # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
+ # a string of space-separated integers. This designates the field
+ # indexes (1-based) of the fields that have indexes on the table.
+ # Here, we skip any indexes across multiple fields.
+ if ' ' in row[1]:
+ continue
+ indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
+ return indexes
-def get_indexes(cursor, table_name):
- """
- Returns a dictionary of fieldname -> infodict for the given table,
- where each infodict is in the format:
- {'primary_key': boolean representing whether it's the primary key,
- 'unique': boolean representing whether it's a unique index}
- """
- # This query retrieves each index on the given table, including the
- # first associated field name
- cursor.execute("""
- SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
- FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
- pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
- WHERE c.oid = idx.indrelid
- AND idx.indexrelid = c2.oid
- AND attr.attrelid = c.oid
- AND attr.attnum = idx.indkey[0]
- AND c.relname = %s""", [table_name])
- indexes = {}
- for row in cursor.fetchall():
- # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
- # a string of space-separated integers. This designates the field
- # indexes (1-based) of the fields that have indexes on the table.
- # Here, we skip any indexes across multiple fields.
- if ' ' in row[1]:
- continue
- indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
- return indexes
-
-# Maps type codes to Django Field types.
-DATA_TYPES_REVERSE = {
- 16: 'BooleanField',
- 21: 'SmallIntegerField',
- 23: 'IntegerField',
- 25: 'TextField',
- 701: 'FloatField',
- 869: 'IPAddressField',
- 1043: 'CharField',
- 1082: 'DateField',
- 1083: 'TimeField',
- 1114: 'DateTimeField',
- 1184: 'DateTimeField',
- 1266: 'TimeField',
- 1700: 'DecimalField',
-}