Loading django/db/backends/__init__.py +8 −0 Original line number Diff line number Diff line Loading @@ -312,6 +312,11 @@ class BaseDatabaseWrapper(object): def make_debug_cursor(self, cursor): return util.CursorDebugWrapper(cursor, self) def schema_editor(self): "Returns a new instance of this backend's SchemaEditor" raise NotImplementedError() class BaseDatabaseFeatures(object): allows_group_by_pk = False # True if django.db.backend.utils.typecast_timestamp is used on values Loading Loading @@ -411,6 +416,9 @@ class BaseDatabaseFeatures(object): # Support for the DISTINCT ON clause can_distinct_on_fields = False # Can we roll back DDL in a transaction? can_rollback_ddl = False def __init__(self, connection): self.connection = connection Loading django/db/backends/postgresql_psycopg2/base.py +6 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ from django.db.backends.postgresql_psycopg2.client import DatabaseClient from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation from django.db.backends.postgresql_psycopg2.version import get_version from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor from django.utils.log import getLogger from django.utils.safestring import SafeUnicode, SafeString from django.utils.timezone import utc Loading Loading @@ -83,6 +84,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): has_bulk_insert = True supports_tablespaces = True can_distinct_on_fields = True can_rollback_ddl = True class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'postgresql' Loading Loading @@ -235,3 +237,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): return self.connection.commit() except Database.IntegrityError as e: raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2] def schema_editor(self): "Returns a new instance of this backend's SchemaEditor" return DatabaseSchemaEditor(self) django/db/backends/postgresql_psycopg2/schema.py 0 → 100644 +5 −0 Original line number Diff line number Diff line from django.db.backends.schema import BaseDatabaseSchemaEditor class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): pass django/db/backends/schema.py 0 → 100644 +178 −0 Original line number Diff line number Diff line import sys import time from django.conf import settings from django.db import transaction from django.db.utils import load_backend from django.utils.log import getLogger logger = getLogger('django.db.backends.schema') class BaseDatabaseSchemaEditor(object): """ This class (and its subclasses) are responsible for emitting schema-changing statements to the databases - model creation/removal/alteration, field renaming, index fiddling, and so on. It is intended to eventually completely replace DatabaseCreation. This class should be used by creating an instance for each set of schema changes (e.g. a syncdb run, a migration file), and by first calling start(), then the relevant actions, and then commit(). This is necessary to allow things like circular foreign key references - FKs will only be created once commit() is called. """ # Overrideable SQL templates sql_create_table = "CREATE TABLE %(table)s (%(definition)s)" sql_rename_table = "ALTER TABLE %(old_table)s RENAME TO %(new_table)s" sql_delete_table = "DROP TABLE %(table)s CASCADE" sql_create_column = "ALTER TABLE %(table)s ADD COLUMN %(definition)s" sql_alter_column_type = "ALTER COLUMN %(column)s TYPE %(type)s" sql_alter_column_null = "ALTER COLUMN %(column)s DROP NOT NULL" sql_alter_column_not_null = "ALTER COLUMN %(column)s SET NOT NULL" sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s CASCADE;" sql_create_check = "ADD CONSTRAINT %(name)s CHECK (%(check)s)" sql_delete_check = "ALTER TABLE %(table)s DROP CONSTRAINT %(name)s" sql_create_unique = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s UNIQUE (%(columns)s)" sql_delete_unique = "ALTER TABLE %(table)s DROP CONSTRAINT %(name)s" sql_create_fk = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s FOREIGN KEY (%(column)s) REFERENCES %(to_table)s (%(to_column)s) DEFERRABLE INITIALLY DEFERRED" sql_delete_fk = "ALTER TABLE %(table)s DROP CONSTRAINT %(name)s" sql_create_index = "CREATE %(unique)s INDEX %(name)s ON %(table)s (%(columns)s)%s;" sql_delete_index = "DROP INDEX %(name)s" sql_create_pk = "ALTER TABLE %(table)s ADD CONSTRAINT %(constraint)s PRIMARY KEY (%(columns)s)" sql_delete_pk = "ALTER TABLE %(table)s DROP CONSTRAINT %(constraint)s" def __init__(self, connection): self.connection = connection # State-managing methods def start(self): "Marks the start of a schema-altering run" self.deferred_sql = [] self.connection.commit_unless_managed() self.connection.enter_transaction_management() self.connection.managed(True) def commit(self): "Finishes a schema-altering run" for sql in self.deferred_sql: self.execute(sql) self.connection.commit() self.connection.leave_transaction_management() def rollback(self): "Tries to roll back a schema-altering run. Call instead of commit()" if not self.connection.features.can_rollback_ddl: raise RuntimeError("Cannot rollback schema changes on this backend") self.connection.rollback() self.connection.leave_transaction_management() # Core utility functions def execute(self, sql, params=[], fetch_results=False): """ Executes the given SQL statement, with optional parameters. """ # Get the cursor cursor = self.connection.cursor() # Log the command we're running, then run it logger.info("%s; (params %r)" % (sql, params)) cursor.execute(sql, params) def quote_name(self, name): return self.connection.ops.quote_name(name) # Actions def create_model(self, model): """ Takes a model and creates a table for it in the database. Will also create any accompanying indexes or unique constraints. """ # Do nothing if this is an unmanaged or proxy model if not model._meta.managed or model._meta.proxy: return [], {} # Create column SQL, add FK deferreds if needed column_sqls = [] for field in model._meta.local_fields: # SQL definition = self.column_sql(model, field) if definition is None: continue column_sqls.append("%s %s" % ( self.quote_name(field.column), definition, )) # FK if field.rel: to_table = field.rel.to._meta.db_table to_column = field.rel.to._meta.get_field(field.rel.field_name).column self.deferred_sql.append( self.sql_create_fk % { "name": '%s_refs_%s_%x' % ( field.column, to_column, abs(hash((model._meta.db_table, to_table))) ), "table": self.quote_name(model._meta.db_table), "column": self.quote_name(field.column), "to_table": self.quote_name(to_table), "to_column": self.quote_name(to_column), } ) # Make the table sql = self.sql_create_table % { "table": model._meta.db_table, "definition": ", ".join(column_sqls) } self.execute(sql) def column_sql(self, model, field, include_default=False): """ Takes a field and returns its column definition. The field must already have had set_attributes_from_name called. """ # Get the column's type and use that as the basis of the SQL sql = field.db_type(connection=self.connection) # Check for fields that aren't actually columns (e.g. M2M) if sql is None: return None # Optionally add the tablespace if it's an implicitly indexed column tablespace = field.db_tablespace or model._meta.db_tablespace if tablespace and self.connection.features.supports_tablespaces and field.unique: sql += " %s" % self.connection.ops.tablespace_sql(tablespace, inline=True) # Work out nullability null = field.null # Oracle treats the empty string ('') as null, so coerce the null # option whenever '' is a possible value. if (field.empty_strings_allowed and not field.primary_key and self.connection.features.interprets_empty_strings_as_nulls): null = True if null: sql += " NULL" else: sql += " NOT NULL" # Primary key/unique outputs if field.primary_key: sql += " PRIMARY KEY" elif field.unique: sql += " UNIQUE" # If we were told to include a default value, do so if include_default: raise NotImplementedError() # Return the sql return sql def delete_model(self, model): self.execute(self.sql_delete_table % { "table": self.quote_name(model._meta.db_table), }) tests/modeltests/schema/__init__.py 0 → 100644 +0 −0 Empty file added. Loading
django/db/backends/__init__.py +8 −0 Original line number Diff line number Diff line Loading @@ -312,6 +312,11 @@ class BaseDatabaseWrapper(object): def make_debug_cursor(self, cursor): return util.CursorDebugWrapper(cursor, self) def schema_editor(self): "Returns a new instance of this backend's SchemaEditor" raise NotImplementedError() class BaseDatabaseFeatures(object): allows_group_by_pk = False # True if django.db.backend.utils.typecast_timestamp is used on values Loading Loading @@ -411,6 +416,9 @@ class BaseDatabaseFeatures(object): # Support for the DISTINCT ON clause can_distinct_on_fields = False # Can we roll back DDL in a transaction? can_rollback_ddl = False def __init__(self, connection): self.connection = connection Loading
django/db/backends/postgresql_psycopg2/base.py +6 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ from django.db.backends.postgresql_psycopg2.client import DatabaseClient from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation from django.db.backends.postgresql_psycopg2.version import get_version from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor from django.utils.log import getLogger from django.utils.safestring import SafeUnicode, SafeString from django.utils.timezone import utc Loading Loading @@ -83,6 +84,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): has_bulk_insert = True supports_tablespaces = True can_distinct_on_fields = True can_rollback_ddl = True class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'postgresql' Loading Loading @@ -235,3 +237,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): return self.connection.commit() except Database.IntegrityError as e: raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2] def schema_editor(self): "Returns a new instance of this backend's SchemaEditor" return DatabaseSchemaEditor(self)
django/db/backends/postgresql_psycopg2/schema.py 0 → 100644 +5 −0 Original line number Diff line number Diff line from django.db.backends.schema import BaseDatabaseSchemaEditor class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): pass
django/db/backends/schema.py 0 → 100644 +178 −0 Original line number Diff line number Diff line import sys import time from django.conf import settings from django.db import transaction from django.db.utils import load_backend from django.utils.log import getLogger logger = getLogger('django.db.backends.schema') class BaseDatabaseSchemaEditor(object): """ This class (and its subclasses) are responsible for emitting schema-changing statements to the databases - model creation/removal/alteration, field renaming, index fiddling, and so on. It is intended to eventually completely replace DatabaseCreation. This class should be used by creating an instance for each set of schema changes (e.g. a syncdb run, a migration file), and by first calling start(), then the relevant actions, and then commit(). This is necessary to allow things like circular foreign key references - FKs will only be created once commit() is called. """ # Overrideable SQL templates sql_create_table = "CREATE TABLE %(table)s (%(definition)s)" sql_rename_table = "ALTER TABLE %(old_table)s RENAME TO %(new_table)s" sql_delete_table = "DROP TABLE %(table)s CASCADE" sql_create_column = "ALTER TABLE %(table)s ADD COLUMN %(definition)s" sql_alter_column_type = "ALTER COLUMN %(column)s TYPE %(type)s" sql_alter_column_null = "ALTER COLUMN %(column)s DROP NOT NULL" sql_alter_column_not_null = "ALTER COLUMN %(column)s SET NOT NULL" sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s CASCADE;" sql_create_check = "ADD CONSTRAINT %(name)s CHECK (%(check)s)" sql_delete_check = "ALTER TABLE %(table)s DROP CONSTRAINT %(name)s" sql_create_unique = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s UNIQUE (%(columns)s)" sql_delete_unique = "ALTER TABLE %(table)s DROP CONSTRAINT %(name)s" sql_create_fk = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s FOREIGN KEY (%(column)s) REFERENCES %(to_table)s (%(to_column)s) DEFERRABLE INITIALLY DEFERRED" sql_delete_fk = "ALTER TABLE %(table)s DROP CONSTRAINT %(name)s" sql_create_index = "CREATE %(unique)s INDEX %(name)s ON %(table)s (%(columns)s)%s;" sql_delete_index = "DROP INDEX %(name)s" sql_create_pk = "ALTER TABLE %(table)s ADD CONSTRAINT %(constraint)s PRIMARY KEY (%(columns)s)" sql_delete_pk = "ALTER TABLE %(table)s DROP CONSTRAINT %(constraint)s" def __init__(self, connection): self.connection = connection # State-managing methods def start(self): "Marks the start of a schema-altering run" self.deferred_sql = [] self.connection.commit_unless_managed() self.connection.enter_transaction_management() self.connection.managed(True) def commit(self): "Finishes a schema-altering run" for sql in self.deferred_sql: self.execute(sql) self.connection.commit() self.connection.leave_transaction_management() def rollback(self): "Tries to roll back a schema-altering run. Call instead of commit()" if not self.connection.features.can_rollback_ddl: raise RuntimeError("Cannot rollback schema changes on this backend") self.connection.rollback() self.connection.leave_transaction_management() # Core utility functions def execute(self, sql, params=[], fetch_results=False): """ Executes the given SQL statement, with optional parameters. """ # Get the cursor cursor = self.connection.cursor() # Log the command we're running, then run it logger.info("%s; (params %r)" % (sql, params)) cursor.execute(sql, params) def quote_name(self, name): return self.connection.ops.quote_name(name) # Actions def create_model(self, model): """ Takes a model and creates a table for it in the database. Will also create any accompanying indexes or unique constraints. """ # Do nothing if this is an unmanaged or proxy model if not model._meta.managed or model._meta.proxy: return [], {} # Create column SQL, add FK deferreds if needed column_sqls = [] for field in model._meta.local_fields: # SQL definition = self.column_sql(model, field) if definition is None: continue column_sqls.append("%s %s" % ( self.quote_name(field.column), definition, )) # FK if field.rel: to_table = field.rel.to._meta.db_table to_column = field.rel.to._meta.get_field(field.rel.field_name).column self.deferred_sql.append( self.sql_create_fk % { "name": '%s_refs_%s_%x' % ( field.column, to_column, abs(hash((model._meta.db_table, to_table))) ), "table": self.quote_name(model._meta.db_table), "column": self.quote_name(field.column), "to_table": self.quote_name(to_table), "to_column": self.quote_name(to_column), } ) # Make the table sql = self.sql_create_table % { "table": model._meta.db_table, "definition": ", ".join(column_sqls) } self.execute(sql) def column_sql(self, model, field, include_default=False): """ Takes a field and returns its column definition. The field must already have had set_attributes_from_name called. """ # Get the column's type and use that as the basis of the SQL sql = field.db_type(connection=self.connection) # Check for fields that aren't actually columns (e.g. M2M) if sql is None: return None # Optionally add the tablespace if it's an implicitly indexed column tablespace = field.db_tablespace or model._meta.db_tablespace if tablespace and self.connection.features.supports_tablespaces and field.unique: sql += " %s" % self.connection.ops.tablespace_sql(tablespace, inline=True) # Work out nullability null = field.null # Oracle treats the empty string ('') as null, so coerce the null # option whenever '' is a possible value. if (field.empty_strings_allowed and not field.primary_key and self.connection.features.interprets_empty_strings_as_nulls): null = True if null: sql += " NULL" else: sql += " NOT NULL" # Primary key/unique outputs if field.primary_key: sql += " PRIMARY KEY" elif field.unique: sql += " UNIQUE" # If we were told to include a default value, do so if include_default: raise NotImplementedError() # Return the sql return sql def delete_model(self, model): self.execute(self.sql_delete_table % { "table": self.quote_name(model._meta.db_table), })