Ticket #6148: generic-db_schema-r8463.diff
File generic-db_schema-r8463.diff, 20.1 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. … … 382 389 cursor = self.connection.cursor() 383 390 return self.get_table_list(cursor) 384 391 392 def schema_name_converter(self, name): 393 """Apply a conversion to the name for the purposes of comparison. 394 395 The default schema name converter is for case sensitive comparison. 396 """ 397 return name 398 399 def get_schema_list(self, cursor): 400 "Returns a list of schemas that exist in the database" 401 return [] 402 403 def get_schema_table_list(self, cursor, schema): 404 "Returns a list of tables in a specific schema" 405 return [] 406 407 def schema_names(self): 408 cursor = self.connection.cursor() 409 return self.get_schema_list(cursor) 410 411 def schema_table_names(self, schema): 412 "Returns a list of names of all tables that exist in the database schema." 413 cursor = self.connection.cursor() 414 return self.get_schema_table_list(cursor, schema) 415 385 416 def django_table_names(self, only_existing=False): 386 417 """ 387 418 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
100 100 def quote_name(self, name): 101 101 if name.startswith("`") and name.endswith("`"): 102 102 return name # Quoting once is enough. 103 return "`%s`" % name 103 # add support for tablenames passed that also have their schema in their name 104 return "`%s`" % name.replace('.','`.`') 104 105 106 def prep_db_table(self, db_schema, db_table): 107 return "%s.%s" % (db_schema, db_table) 108 105 109 def random_function_sql(self): 106 110 return 'RAND()' 107 111 -
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
32 32 cursor.execute("SHOW TABLES") 33 33 return [row[0] for row in cursor.fetchall()] 34 34 35 def get_schema_list(self, cursor): 36 cursor.execute("SHOW SCHEMAS") 37 return [row[0] for row in cursor.fetchall()] 38 39 def get_schema_table_list(self, cursor, schema): 40 cursor.execute("SHOW TABLES FROM %s" % self.connection.ops.quote_name(schema)) 41 return [schema + "." + row[0] for row in cursor.fetchall()] 42 35 43 def get_table_description(self, cursor, table_name): 36 44 "Returns a description of the table, with the DB-API cursor.description interface." 37 45 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
47 47 return '%s' 48 48 49 49 def last_insert_id(self, cursor, table_name, pk_name): 50 cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) 50 # add support for tablenames passed that also have their schema in their name 51 cursor.execute(("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)).replace('.', '"."')) 51 52 return cursor.fetchone()[0] 52 53 53 54 def no_limit_value(self): … … 56 57 def quote_name(self, name): 57 58 if name.startswith('"') and name.endswith('"'): 58 59 return name # Quoting once is enough. 59 return '"%s"' % name 60 # add support for tablenames passed that also have their schema in their name 61 return '"%s"' % name.replace('.','"."') 60 62 63 def prep_db_table(self, db_schema, db_table): 64 return "%s.%s" % (db_schema, db_table) 65 61 66 def sql_flush(self, style, tables, sequences): 62 67 if tables: 63 68 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/model-api.txt
1210 1210 that aren't allowed in Python variable names -- notably, the hyphen -- 1211 1211 that's OK. Django quotes column and table names behind the scenes. 1212 1212 1213 ``db_schema`` 1214 ----------------- 1215 1216 **New in Django development version** 1217 1218 The name of the database schema to use for the model. If the backend 1219 doesn't support multiple schemas, this options is ignored. 1220 1221 If this is used Django will prefix any table names with the schema name. 1222 For MySQL Django would use ``db_schema + '.' + db_table``. 1223 1224 1213 1225 ``db_tablespace`` 1214 1226 ----------------- 1215 1227 -
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 }