Ticket #6148: generic-db_schema-r8696.diff
File generic-db_schema-r8696.diff, 21.0 KB (added by , 16 years ago) |
---|
-
django/core/management/commands/syncdb.py
61 61 app_name = app.__name__.split('.')[-2] 62 62 model_list = models.get_models(app) 63 63 for model in model_list: 64 # Add model defined schema tables if anny 65 if model._meta.db_schema: 66 tables += connection.introspection.schema_table_names(model._meta.db_schema) 64 67 # Create the model's database table, if it doesn't already exist. 65 68 if verbosity >= 2: 66 69 print "Processing %s.%s model" % (app_name, model._meta.object_name) -
django/core/management/sql.py
84 84 references_to_delete = {} 85 85 app_models = models.get_models(app) 86 86 for model in app_models: 87 schema = model._meta.db_schema 88 # Find aditional tables in model defined schemas 89 if schema: 90 table_names += connection.introspection.get_schema_table_list(cursor, schema) 87 91 if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names: 88 92 # The table exists, so it needs to be dropped 89 93 opts = model._meta -
django/db/backends/__init__.py
225 225 """ 226 226 raise NotImplementedError() 227 227 228 def prep_db_table(self, db_schema, db_table): 229 """ 230 Prepares and formats the table name if neccesary. 231 Just returns the db_table if not supported 232 """ 233 return db_table 234 228 235 def random_function_sql(self): 229 236 """ 230 237 Returns a SQL expression that returns a random value. … … 386 393 cursor = self.connection.cursor() 387 394 return self.get_table_list(cursor) 388 395 396 def schema_name_converter(self, name): 397 """Apply a conversion to the name for the purposes of comparison. 398 399 The default schema name converter is for case sensitive comparison. 400 """ 401 return name 402 403 def get_schema_list(self, cursor): 404 "Returns a list of schemas that exist in the database" 405 return [] 406 407 def get_schema_table_list(self, cursor, schema): 408 "Returns a list of tables in a specific schema" 409 return [] 410 411 def schema_names(self): 412 cursor = self.connection.cursor() 413 return self.get_schema_list(cursor) 414 415 def schema_table_names(self, schema): 416 "Returns a list of names of all tables that exist in the database schema." 417 cursor = self.connection.cursor() 418 return self.get_schema_table_list(cursor, schema) 419 389 420 def django_table_names(self, only_existing=False): 390 421 """ 391 422 Returns a list of all table names that have associated Django models and -
django/db/backends/creation.py
25 25 def __init__(self, connection): 26 26 self.connection = connection 27 27 28 def default_schema(self): 29 return "" 30 31 def sql_create_schema(self, schema, style): 32 """" 33 Returns the SQL required to create a single schema 34 """ 35 qn = self.connection.ops.quote_name 36 output = "%s %s;" % (style.SQL_KEYWORD('CREATE SCHEMA'), qn(schema)) 37 return output 38 28 39 def sql_create_model(self, model, style, known_models=set()): 29 40 """ 30 41 Returns the SQL required to create a single model, as a tuple of: … … 122 133 r_col = f.column 123 134 table = opts.db_table 124 135 col = opts.get_field(f.rel.field_name).column 136 # Add schema if we are related to a model in different schema 137 # and we are not in a different schema ourselfs 138 if rel_opts.db_schema and not opts.db_schema: 139 table = "%s.%s" % (self.default_schema(), table) 125 140 # For MySQL, r_name must be unique in the first 64 characters. 126 141 # So we are careful with character usage here. 127 142 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) … … 243 258 tablespace_sql = '' 244 259 else: 245 260 tablespace_sql = '' 261 # Use original db_table in index name if schema is provided 262 if model._meta.db_schema: 263 index_table_name = model._meta._db_table 264 else: 265 index_table_name = model._meta.db_table 246 266 output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' + 247 style.SQL_TABLE(qn('%s_%s' % ( model._meta.db_table, f.column))) + ' ' +267 style.SQL_TABLE(qn('%s_%s' % (index_table_name, f.column))) + ' ' + 248 268 style.SQL_KEYWORD('ON') + ' ' + 249 269 style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + 250 270 "(%s)" % style.SQL_FIELD(qn(f.column)) + … … 253 273 output = [] 254 274 return output 255 275 276 def sql_destroy_schema(self, schema, style): 277 """" 278 Returns the SQL required to create a single schema 279 """ 280 qn = self.connection.ops.quote_name 281 output = "%s %s CASCADE;" % (style.SQL_KEYWORD('DROP SCHEMA IF EXISTS'), qn(schema)) 282 return output 283 256 284 def sql_destroy_model(self, model, references_to_delete, style): 257 285 "Return the DROP TABLE and restraint dropping statements for a single model" 258 286 # Drop the table now … … 324 352 325 353 return test_database_name 326 354 355 def _create_test_schemas(self, verbosity, schemas, cursor): 356 from django.core.management.color import color_style 357 style = color_style() 358 for schema in schemas: 359 if verbosity >= 1: 360 print "Creating schema %s" % schema 361 cursor.execute(self.sql_create_schema(schema, style)) 362 363 def _destroy_test_schemas(self, verbosity, schemas, cursor): 364 from django.core.management.color import color_style 365 style = color_style() 366 for schema in schemas: 367 if verbosity >= 1: 368 print "Destroying schema %s" % schema 369 cursor.execute(self.sql_destroy_schema(schema, style)) 370 if verbosity >= 1: 371 print "Schema %s destroyed" % schema 372 373 def _get_schemas(self, apps): 374 from django.db import models 375 schemas = set() 376 for app in apps: 377 app_models = models.get_models(app) 378 for model in app_models: 379 schema = model._meta.db_schema 380 if not schema or schema in schemas: 381 continue 382 schemas.add(schema) 383 return schemas 384 385 def _get_app_with_schemas(self): 386 from django.db import models 387 apps = models.get_apps() 388 schema_apps = set() 389 for app in apps: 390 app_models = models.get_models(app) 391 for model in app_models: 392 schema = model._meta.db_schema 393 if not schema or app in schema_apps: 394 continue 395 schema_apps.add(app) 396 continue 397 398 return schema_apps 399 327 400 def _create_test_db(self, verbosity, autoclobber): 328 401 "Internal implementation - creates the test db tables." 402 schema_apps = self._get_app_with_schemas() 403 schemas = self._get_schemas(schema_apps) 329 404 suffix = self.sql_table_creation_suffix() 330 405 331 406 if settings.TEST_DATABASE_NAME: … … 342 417 self.set_autocommit() 343 418 try: 344 419 cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix)) 420 #Connect to the new database to create schemas in it 421 self.connection.close() 422 settings.DATABASE_NAME = test_database_name 423 cursor = self.connection.cursor() 424 self.set_autocommit() 425 self._create_test_schemas(verbosity, schemas, cursor) 345 426 except Exception, e: 346 427 sys.stderr.write("Got an error creating the test database: %s\n" % e) 347 428 if not autoclobber: … … 350 431 try: 351 432 if verbosity >= 1: 352 433 print "Destroying old test database..." 434 self._destroy_test_schemas(verbosity, schemas, cursor) 353 435 cursor.execute("DROP DATABASE %s" % qn(test_database_name)) 354 436 if verbosity >= 1: 355 437 print "Creating test database..." 356 438 cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix)) 439 #Connect to the new database to create schemas in it 440 self.connection.close() 441 settings.DATABASE_NAME = test_database_name 442 cursor = self.connection.cursor() 443 self.set_autocommit() 444 self._create_test_schemas(verbosity, schemas, cursor) 357 445 except Exception, e: 358 446 sys.stderr.write("Got an error recreating the test database: %s\n" % e) 359 447 sys.exit(2) -
django/db/backends/mysql/base.py
142 142 def quote_name(self, name): 143 143 if name.startswith("`") and name.endswith("`"): 144 144 return name # Quoting once is enough. 145 return "`%s`" % name 145 # add support for tablenames passed that also have their schema in their name 146 return "`%s`" % name.replace('.','`.`') 146 147 148 def prep_db_table(self, db_schema, db_table): 149 return "%s.%s" % (db_schema, db_table) 150 147 151 def random_function_sql(self): 148 152 return 'RAND()' 149 153 -
django/db/backends/mysql/creation.py
65 65 field.rel.to._meta.db_table, field.rel.to._meta.pk.column) 66 66 ] 67 67 return table_output, deferred 68 69 No newline at end of file 68 69 def default_schema(self): 70 return settings.DATABASE_NAME 71 72 def sql_create_schema(self, schema, style): 73 """ 74 Returns the SQL required to create a single schema. 75 In MySQL schemas are synonymous to databases 76 """ 77 qn = self.connection.ops.quote_name 78 output = "%s %s;" % (style.SQL_KEYWORD('CREATE DATABASE'), qn(schema)) 79 return output 80 81 def sql_destroy_schema(self, schema, style): 82 """" 83 Returns the SQL required to create a single schema 84 """ 85 qn = self.connection.ops.quote_name 86 output = "%s %s;" % (style.SQL_KEYWORD('DROP DATABASE IF EXISTS'), qn(schema)) 87 return output 88 No newline at end of file -
django/db/backends/mysql/introspection.py
33 33 cursor.execute("SHOW TABLES") 34 34 return [row[0] for row in cursor.fetchall()] 35 35 36 def get_schema_list(self, cursor): 37 cursor.execute("SHOW SCHEMAS") 38 return [row[0] for row in cursor.fetchall()] 39 40 def get_schema_table_list(self, cursor, schema): 41 cursor.execute("SHOW TABLES FROM %s" % self.connection.ops.quote_name(schema)) 42 return [schema + "." + row[0] for row in cursor.fetchall()] 43 36 44 def get_table_description(self, cursor, table_name): 37 45 "Returns a description of the table, with the DB-API cursor.description interface." 38 46 cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)) -
django/db/backends/postgresql/introspection.py
29 29 AND pg_catalog.pg_table_is_visible(c.oid)""") 30 30 return [row[0] for row in cursor.fetchall()] 31 31 32 def get_schema_list(self, cursor): 33 cursor.execute(""" 34 SELECT DISTINCT n.nspname 35 FROM pg_catalog.pg_class c 36 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace 37 WHERE c.relkind IN ('r', 'v', '') 38 AND n.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')""") 39 return [row[0] for row in cursor.fetchall()] 40 41 def get_schema_table_list(self, cursor, schema): 42 cursor.execute(""" 43 SELECT c.relname 44 FROM pg_catalog.pg_class c 45 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace 46 WHERE c.relkind IN ('r', 'v', '') 47 AND n.nspname = '%s'""" % schema) 48 return [schema + "." + row[0] for row in cursor.fetchall()] 49 32 50 def get_table_description(self, cursor, table_name): 33 51 "Returns a description of the table, with the DB-API cursor.description interface." 34 52 cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)) -
django/db/backends/postgresql/operations.py
55 55 return '%s' 56 56 57 57 def last_insert_id(self, cursor, table_name, pk_name): 58 cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) 58 # add support for tablenames passed that also have their schema in their name 59 cursor.execute(("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)).replace('.', '"."')) 59 60 return cursor.fetchone()[0] 60 61 61 62 def no_limit_value(self): … … 64 65 def quote_name(self, name): 65 66 if name.startswith('"') and name.endswith('"'): 66 67 return name # Quoting once is enough. 67 return '"%s"' % name 68 # add support for tablenames passed that also have their schema in their name 69 return '"%s"' % name.replace('.','"."') 68 70 71 def prep_db_table(self, db_schema, db_table): 72 return "%s.%s" % (db_schema, db_table) 73 69 74 def sql_flush(self, style, tables, sequences): 70 75 if tables: 71 76 if self.postgres_version[0] >= 8 and self.postgres_version[1] >= 1: -
django/db/models/options.py
21 21 DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', 22 22 'unique_together', 'permissions', 'get_latest_by', 23 23 'order_with_respect_to', 'app_label', 'db_tablespace', 24 'abstract' )24 'abstract', 'db_schema') 25 25 26 26 class Options(object): 27 27 def __init__(self, meta, app_label=None): … … 29 29 self.module_name, self.verbose_name = None, None 30 30 self.verbose_name_plural = None 31 31 self.db_table = '' 32 self.db_schema = '' 32 33 self.ordering = [] 33 34 self.unique_together = [] 34 35 self.permissions = [] … … 95 96 self.db_table = "%s_%s" % (self.app_label, self.module_name) 96 97 self.db_table = truncate_name(self.db_table, connection.ops.max_name_length()) 97 98 99 # Patch db_table with the schema if provided and allowed 100 if self.db_schema: 101 # Store original db_table in a save place first 102 self._db_table = self.db_table 103 self.db_table = connection.ops.prep_db_table(self.db_schema, self.db_table) 104 # If no changes were done then backend don't support schemas 105 if self._db_table == self.db_table: 106 self.db_schema = '' 98 107 99 108 def _prepare(self, model): 100 109 if self.order_with_respect_to: -
docs/ref/models/options.txt
42 42 aren't allowed in Python variable names -- notably, the hyphen -- that's OK. 43 43 Django quotes column and table names behind the scenes. 44 44 45 ``db_schema`` 46 ----------------- 47 48 **New in Django development version** 49 50 The name of the database schema to use for the model. If the backend 51 doesn't support multiple schemas, this options is ignored. 52 53 If this is used Django will prefix any table names with the schema name. 54 For example MySQL Django would use ``db_schema + '.' + db_table``. 55 Be aware that postgres supports different schemas within the database. 56 MySQL solves the same thing by treating it as just another database. 57 58 59 45 60 ``db_tablespace`` 46 61 ----------------- 47 62 -
docs/topics/db/models.txt
594 594 verbose_name_plural = "oxen" 595 595 596 596 Model metadata is "anything that's not a field", such as ordering options 597 (:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`), or 597 (:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`), 598 (:attr:`~Options.db_schema`) custom schema for the tables, or 598 599 human-readable singular and plural names (:attr:`~Options.verbose_name` and 599 600 :attr:`~Options.verbose_name_plural`). None are required, and adding ``class 600 601 Meta`` to a model is completely optional. -
tests/modeltests/schemas/__init__.py
1 # -
tests/modeltests/schemas/models.py
1 # coding: utf-8 2 3 from django.db import models 4 5 6 class Blog(models.Model): 7 "Model in default schema" 8 name = models.CharField(max_length=50) 9 10 11 class Entry(models.Model): 12 "Model in custom schema that reference the default" 13 blog = models.ForeignKey(Blog) 14 title = models.CharField(max_length=50) 15 16 class Meta: 17 "using custom db_table as well" 18 db_table='schema_blog_entries' 19 db_schema = 'test_schema' 20 21 22 class Comment(models.Model): 23 "Model in the custom schema that references Entry in the same schema" 24 entry = models.ForeignKey(Entry) 25 text = models.CharField(max_length=50) 26 27 class Meta: 28 db_schema = 'test_schema' 29 30 __test__ = {'API_TESTS': """ 31 32 #Test with actual data 33 # Nothing in there yet 34 >>> Blog.objects.all() 35 [] 36 37 # Create a blog 38 >>> b = Blog(name='Test') 39 >>> b.save() 40 41 # Verify that we got an ID 42 >>> b.id 43 1 44 45 # Create entry 46 >>> e = Entry(blog=b, title='Test entry') 47 >>> e.save() 48 >>> e.id 49 1 50 51 # Create Comments 52 >>> c1 = Comment(entry=e, text='nice entry') 53 >>> c1.save() 54 >>> c2 = Comment(entry=e, text='really like it') 55 >>> c2.save() 56 57 #Retrieve the stuff again. 58 >>> b2 = Blog.objects.get(id=b.id) 59 >>> b==b2 60 True 61 62 >>> b2.entry_set.all() 63 [<Entry: Entry object>] 64 65 >>> from django.conf import settings 66 >>> from django.db import connection, models 67 68 # Test if we support schemas and can find the table if so 69 >>> if e._meta.db_schema: 70 ... tables = connection.introspection.schema_table_names(e._meta.db_schema) 71 ... else: 72 ... tables = connection.introspection.table_names() 73 >>> if connection.introspection.table_name_converter(e._meta.db_table) in tables: 74 ... print "ok" 75 ... else: 76 ... print "schema=" + e._meta.db_schema 77 ... print tables 78 ok 79 80 # Test that all but sqlite3 backend suports schema and doesn't drop it. 81 # Oracle is not tested 82 >>> if settings.DATABASE_ENGINE != 'sqlite3' and settings.DATABASE_ENGINE != 'oracle': 83 ... if e._meta.db_schema != 'test_schema': 84 ... print "shouldn't drop or modify schema" 85 86 >>> from django.core.management.sql import * 87 >>> from django.core.management.color import color_style 88 89 >>> style = color_style() 90 >>> app = models.get_app('schemas') 91 92 # Get the sql_create sequence 93 >>> a = sql_create(app, style) 94 95 96 #Done 97 """ 98 }