Permalink
Browse files

Fixed #5461 -- Refactored the database backend code to use classes fo…

…r 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
  • Loading branch information...
1 parent cec69eb commit 9dc4ba875f21d5690f6ad5995123a67a3c44bafe @freakboy3742 freakboy3742 committed Aug 11, 2008
Showing with 1,521 additions and 1,416 deletions.
  1. +2 −2 django/contrib/gis/db/backend/mysql/creation.py
  2. +1 −3 django/contrib/gis/db/backend/oracle/creation.py
  3. +16 −16 django/contrib/gis/db/backend/postgis/creation.py
  4. +12 −14 django/contrib/gis/management/commands/inspectdb.py
  5. +2 −2 django/core/management/commands/dbshell.py
  6. +6 −8 django/core/management/commands/inspectdb.py
  7. +10 −17 django/core/management/commands/syncdb.py
  8. +2 −2 django/core/management/commands/testserver.py
  9. +20 −310 django/core/management/sql.py
  10. +2 −5 django/core/management/validation.py
  11. +2 −20 django/db/__init__.py
  12. +91 −10 django/db/backends/__init__.py
  13. +392 −3 django/db/backends/creation.py
  14. +21 −6 django/db/backends/dummy/base.py
  15. +0 −3 django/db/backends/dummy/client.py
  16. +0 −1 django/db/backends/dummy/creation.py
  17. +0 −8 django/db/backends/dummy/introspection.py
  18. +14 −4 django/db/backends/mysql/base.py
  19. +24 −22 django/db/backends/mysql/client.py
  20. +68 −28 django/db/backends/mysql/creation.py
  21. +84 −83 django/db/backends/mysql/introspection.py
  22. +13 −0 django/db/backends/mysql/validation.py
  23. +15 −7 django/db/backends/oracle/base.py
  24. +10 −8 django/db/backends/oracle/client.py
  25. +257 −259 django/db/backends/oracle/creation.py
  26. +93 −88 django/db/backends/oracle/introspection.py
  27. +16 −7 django/db/backends/postgresql/base.py
  28. +14 −12 django/db/backends/postgresql/client.py
  29. +38 −28 django/db/backends/postgresql/creation.py
  30. +81 −81 django/db/backends/postgresql/introspection.py
  31. +15 −3 django/db/backends/postgresql_psycopg2/base.py
  32. +0 −1 django/db/backends/postgresql_psycopg2/client.py
  33. +0 −1 django/db/backends/postgresql_psycopg2/creation.py
  34. +19 −81 django/db/backends/postgresql_psycopg2/introspection.py
  35. +16 −6 django/db/backends/sqlite3/base.py
  36. +5 −3 django/db/backends/sqlite3/client.py
  37. +73 −27 django/db/backends/sqlite3/creation.py
  38. +74 −74 django/db/backends/sqlite3/introspection.py
  39. +3 −3 django/db/models/fields/__init__.py
  40. +3 −3 django/test/simple.py
  41. +1 −150 django/test/utils.py
  42. +4 −1 docs/testing.txt
  43. +2 −6 tests/regressiontests/backends/models.py
@@ -1,5 +1,5 @@
-from django.test.utils import create_test_db
def create_spatial_db(test=True, verbosity=1, autoclobber=False):
if not test: raise NotImplementedError('This uses `create_test_db` from test/utils.py')
- create_test_db(verbosity, autoclobber)
+ from django.db import connection
+ connection.creation.create_test_db(verbosity, autoclobber)
@@ -1,8 +1,6 @@
-from django.db.backends.oracle.creation import create_test_db
def create_spatial_db(test=True, verbosity=1, autoclobber=False):
"A wrapper over the Oracle `create_test_db` routine."
if not test: raise NotImplementedError('This uses `create_test_db` from db/backends/oracle/creation.py')
- from django.conf import settings
from django.db import connection
- create_test_db(settings, connection, verbosity, autoclobber)
+ connection.creation.create_test_db(verbosity, autoclobber)
@@ -1,7 +1,7 @@
from django.conf import settings
from django.core.management import call_command
from django.db import connection
-from django.test.utils import _set_autocommit, TEST_DATABASE_PREFIX
+from django.db.backends.creation import TEST_DATABASE_PREFIX
import os, re, sys
def getstatusoutput(cmd):
@@ -38,9 +38,9 @@ def _create_with_cursor(db_name, verbosity=1, autoclobber=False):
create_sql = 'CREATE DATABASE %s' % connection.ops.quote_name(db_name)
if settings.DATABASE_USER:
create_sql += ' OWNER %s' % settings.DATABASE_USER
-
+
cursor = connection.cursor()
- _set_autocommit(connection)
+ connection.creation.set_autocommit(connection)
try:
# Trying to create the database first.
@@ -58,12 +58,12 @@ def _create_with_cursor(db_name, verbosity=1, autoclobber=False):
else:
raise Exception('Spatial Database Creation canceled.')
foo = _create_with_cursor
-
+
created_regex = re.compile(r'^createdb: database creation failed: ERROR: database ".+" already exists')
def _create_with_shell(db_name, verbosity=1, autoclobber=False):
"""
- If no spatial database already exists, then using a cursor will not work.
- Thus, a `createdb` command will be issued through the shell to bootstrap
+ If no spatial database already exists, then using a cursor will not work.
+ Thus, a `createdb` command will be issued through the shell to bootstrap
creation of the spatial database.
"""
@@ -83,7 +83,7 @@ def _create_with_shell(db_name, verbosity=1, autoclobber=False):
if verbosity >= 1: print 'Destroying old spatial database...'
drop_cmd = 'dropdb %s%s' % (options, db_name)
status, output = getstatusoutput(drop_cmd)
- if status != 0:
+ if status != 0:
raise Exception('Could not drop database %s: %s' % (db_name, output))
if verbosity >= 1: print 'Creating new spatial database...'
status, output = getstatusoutput(create_cmd)
@@ -102,10 +102,10 @@ def create_spatial_db(test=False, verbosity=1, autoclobber=False, interactive=Fa
raise Exception('Spatial database creation only supported postgresql_psycopg2 platform.')
# Getting the spatial database name
- if test:
+ if test:
db_name = get_spatial_db(test=True)
_create_with_cursor(db_name, verbosity=verbosity, autoclobber=autoclobber)
- else:
+ else:
db_name = get_spatial_db()
_create_with_shell(db_name, verbosity=verbosity, autoclobber=autoclobber)
@@ -125,7 +125,7 @@ def create_spatial_db(test=False, verbosity=1, autoclobber=False, interactive=Fa
# Syncing the database
call_command('syncdb', verbosity=verbosity, interactive=interactive)
-
+
def drop_db(db_name=False, test=False):
"""
Drops the given database (defaults to what is returned from
@@ -151,7 +151,7 @@ def get_cmd_options(db_name):
def get_spatial_db(test=False):
"""
- Returns the name of the spatial database. The 'test' keyword may be set
+ Returns the name of the spatial database. The 'test' keyword may be set
to return the test spatial database name.
"""
if test:
@@ -167,13 +167,13 @@ def get_spatial_db(test=False):
def load_postgis_sql(db_name, verbosity=1):
"""
- This routine loads up the PostGIS SQL files lwpostgis.sql and
+ This routine loads up the PostGIS SQL files lwpostgis.sql and
spatial_ref_sys.sql.
"""
# Getting the path to the PostGIS SQL
try:
- # POSTGIS_SQL_PATH may be placed in settings to tell GeoDjango where the
+ # POSTGIS_SQL_PATH may be placed in settings to tell GeoDjango where the
# PostGIS SQL files are located. This is especially useful on Win32
# platforms since the output of pg_config looks like "C:/PROGRA~1/..".
sql_path = settings.POSTGIS_SQL_PATH
@@ -193,7 +193,7 @@ def load_postgis_sql(db_name, verbosity=1):
# Getting the psql command-line options, and command format.
options = get_cmd_options(db_name)
cmd_fmt = 'psql %s-f "%%s"' % options
-
+
# Now trying to load up the PostGIS functions
cmd = cmd_fmt % lwpostgis_file
if verbosity >= 1: print cmd
@@ -211,8 +211,8 @@ def load_postgis_sql(db_name, verbosity=1):
# Setting the permissions because on Windows platforms the owner
# of the spatial_ref_sys and geometry_columns tables is always
# the postgres user, regardless of how the db is created.
- if os.name == 'nt': set_permissions(db_name)
-
+ if os.name == 'nt': set_permissions(db_name)
+
def set_permissions(db_name):
"""
Sets the permissions on the given database to that of the user specified
@@ -7,7 +7,7 @@
from django.contrib.gis.db.backend import SpatialBackend
class Command(InspectCommand):
-
+
# Mapping from lower-case OGC type to the corresponding GeoDjango field.
geofield_mapping = {'point' : 'PointField',
'linestring' : 'LineStringField',
@@ -21,11 +21,11 @@ class Command(InspectCommand):
def geometry_columns(self):
"""
- Returns a datastructure of metadata information associated with the
+ Returns a datastructure of metadata information associated with the
`geometry_columns` (or equivalent) table.
"""
# The `geo_cols` is a dictionary data structure that holds information
- # about any geographic columns in the database.
+ # about any geographic columns in the database.
geo_cols = {}
def add_col(table, column, coldata):
if table in geo_cols:
@@ -47,7 +47,7 @@ def add_col(table, column, coldata):
elif SpatialBackend.name == 'mysql':
# On MySQL have to get all table metadata before hand; this means walking through
# each table and seeing if any column types are spatial. Can't detect this with
- # `cursor.description` (what the introspection module does) because all spatial types
+ # `cursor.description` (what the introspection module does) because all spatial types
# have the same integer type (255 for GEOMETRY).
from django.db import connection
cursor = connection.cursor()
@@ -67,13 +67,11 @@ def add_col(table, column, coldata):
def handle_inspection(self):
"Overloaded from Django's version to handle geographic database tables."
- from django.db import connection, get_introspection_module
+ from django.db import connection
import keyword
- introspection_module = get_introspection_module()
-
geo_cols = self.geometry_columns()
-
+
table2model = lambda table_name: table_name.title().replace('_', '')
cursor = connection.cursor()
@@ -88,20 +86,20 @@ def handle_inspection(self):
yield ''
yield 'from django.contrib.gis.db import models'
yield ''
- for table_name in introspection_module.get_table_list(cursor):
+ for table_name in connection.introspection.get_table_list(cursor):
# Getting the geographic table dictionary.
geo_table = geo_cols.get(table_name, {})
yield 'class %s(models.Model):' % table2model(table_name)
try:
- relations = introspection_module.get_relations(cursor, table_name)
+ relations = connection.introspection.get_relations(cursor, table_name)
except NotImplementedError:
relations = {}
try:
- indexes = introspection_module.get_indexes(cursor, table_name)
+ indexes = connection.introspection.get_indexes(cursor, table_name)
except NotImplementedError:
indexes = {}
- for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)):
+ for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
att_name, iatt_name = row[0].lower(), row[0]
comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
extra_params = {} # Holds Field parameters such as 'db_column'.
@@ -133,12 +131,12 @@ def handle_inspection(self):
if srid != 4326: extra_params['srid'] = srid
else:
try:
- field_type = introspection_module.DATA_TYPES_REVERSE[row[1]]
+ field_type = connection.introspection.data_types_reverse[row[1]]
except KeyError:
field_type = 'TextField'
comment_notes.append('This field type is a guess.')
- # 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).
if type(field_type) is tuple:
field_type, new_params = field_type
@@ -6,5 +6,5 @@ class Command(NoArgsCommand):
requires_model_validation = False
def handle_noargs(self, **options):
- from django.db import runshell
- runshell()
+ from django.db import connection
+ connection.client.runshell()
@@ -13,11 +13,9 @@ def handle_noargs(self, **options):
raise CommandError("Database inspection isn't supported for the currently selected database backend.")
def handle_inspection(self):
- from django.db import connection, get_introspection_module
+ from django.db import connection
import keyword
- introspection_module = get_introspection_module()
-
table2model = lambda table_name: table_name.title().replace('_', '')
cursor = connection.cursor()
@@ -32,17 +30,17 @@ def handle_inspection(self):
yield ''
yield 'from django.db import models'
yield ''
- for table_name in introspection_module.get_table_list(cursor):
+ for table_name in connection.introspection.get_table_list(cursor):
yield 'class %s(models.Model):' % table2model(table_name)
try:
- relations = introspection_module.get_relations(cursor, table_name)
+ relations = connection.introspection.get_relations(cursor, table_name)
except NotImplementedError:
relations = {}
try:
- indexes = introspection_module.get_indexes(cursor, table_name)
+ indexes = connection.introspection.get_indexes(cursor, table_name)
except NotImplementedError:
indexes = {}
- for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)):
+ for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
att_name = row[0].lower()
comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
extra_params = {} # Holds Field parameters such as 'db_column'.
@@ -65,7 +63,7 @@ def handle_inspection(self):
extra_params['db_column'] = att_name
else:
try:
- field_type = introspection_module.DATA_TYPES_REVERSE[row[1]]
+ field_type = connection.introspection.data_types_reverse[row[1]]
except KeyError:
field_type = 'TextField'
comment_notes.append('This field type is a guess.')
@@ -21,7 +21,7 @@ class Command(NoArgsCommand):
def handle_noargs(self, **options):
from django.db import connection, transaction, models
from django.conf import settings
- from django.core.management.sql import table_names, installed_models, sql_model_create, sql_for_pending_references, many_to_many_sql_for_model, custom_sql_for_model, sql_indexes_for_model, emit_post_sync_signal
+ from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal
verbosity = int(options.get('verbosity', 1))
interactive = options.get('interactive')
@@ -50,16 +50,9 @@ def handle_noargs(self, **options):
cursor = connection.cursor()
- if connection.features.uses_case_insensitive_names:
- table_name_converter = lambda x: x.upper()
- else:
- table_name_converter = lambda x: x
- # Get a list of all existing database tables, so we know what needs to
- # be added.
- tables = [table_name_converter(name) for name in table_names()]
-
# Get a list of already installed *models* so that references work right.
- seen_models = installed_models(tables)
+ tables = connection.introspection.table_names()
+ seen_models = connection.introspection.installed_models(tables)
created_models = set()
pending_references = {}
@@ -71,21 +64,21 @@ def handle_noargs(self, **options):
# Create the model's database table, if it doesn't already exist.
if verbosity >= 2:
print "Processing %s.%s model" % (app_name, model._meta.object_name)
- if table_name_converter(model._meta.db_table) in tables:
+ if connection.introspection.table_name_converter(model._meta.db_table) in tables:
continue
- sql, references = sql_model_create(model, self.style, seen_models)
+ sql, references = connection.creation.sql_create_model(model, self.style, seen_models)
seen_models.add(model)
created_models.add(model)
for refto, refs in references.items():
pending_references.setdefault(refto, []).extend(refs)
if refto in seen_models:
- sql.extend(sql_for_pending_references(refto, self.style, pending_references))
- sql.extend(sql_for_pending_references(model, self.style, pending_references))
+ sql.extend(connection.creation.sql_for_pending_references(refto, self.style, pending_references))
+ sql.extend(connection.creation.sql_for_pending_references(model, self.style, pending_references))
if verbosity >= 1:
print "Creating table %s" % model._meta.db_table
for statement in sql:
cursor.execute(statement)
- tables.append(table_name_converter(model._meta.db_table))
+ tables.append(connection.introspection.table_name_converter(model._meta.db_table))
# Create the m2m tables. This must be done after all tables have been created
# to ensure that all referred tables will exist.
@@ -94,7 +87,7 @@ def handle_noargs(self, **options):
model_list = models.get_models(app)
for model in model_list:
if model in created_models:
- sql = many_to_many_sql_for_model(model, self.style)
+ sql = connection.creation.sql_for_many_to_many(model, self.style)
if sql:
if verbosity >= 2:
print "Creating many-to-many tables for %s.%s model" % (app_name, model._meta.object_name)
@@ -140,7 +133,7 @@ def handle_noargs(self, **options):
app_name = app.__name__.split('.')[-2]
for model in models.get_models(app):
if model in created_models:
- index_sql = sql_indexes_for_model(model, self.style)
+ index_sql = connection.creation.sql_indexes_for_model(model, self.style)
if index_sql:
if verbosity >= 1:
print "Installing index for %s.%s model" % (app_name, model._meta.object_name)
@@ -18,13 +18,13 @@ class Command(BaseCommand):
def handle(self, *fixture_labels, **options):
from django.core.management import call_command
- from django.test.utils import create_test_db
+ from django.db import connection
verbosity = int(options.get('verbosity', 1))
addrport = options.get('addrport')
# Create a test database.
- db_name = create_test_db(verbosity=verbosity)
+ db_name = connection.creation.create_test_db(verbosity=verbosity)
# Import the fixture data into the test database.
call_command('loaddata', *fixture_labels, **{'verbosity': verbosity})
Oops, something went wrong. Retry.

0 comments on commit 9dc4ba8

Please sign in to comment.