Ticket #5461: 5461-r8225.diff
| File 5461-r8225.diff, 166.1 kB (added by russellm, 5 months ago) |
|---|
-
django/test/simple.py
old new 3 3 from django.db.models import get_app, get_apps 4 4 from django.test import _doctest as doctest 5 5 from django.test.utils import setup_test_environment, teardown_test_environment 6 from django.test.utils import create_test_db, destroy_test_db7 6 from django.test.testcases import OutputChecker, DocTestRunner 8 7 9 8 # The module name for tests outside models.py … … 139 138 suite.addTest(test) 140 139 141 140 old_name = settings.DATABASE_NAME 142 create_test_db(verbosity, autoclobber=not interactive) 141 from django.db import connection 142 connection.creation.create_test_db(verbosity, autoclobber=not interactive) 143 143 result = unittest.TextTestRunner(verbosity=verbosity).run(suite) 144 destroy_test_db(old_name, verbosity)144 connection.creation.destroy_test_db(old_name, verbosity) 145 145 146 146 teardown_test_environment() 147 147 -
django/test/utils.py
old new 1 1 import sys, time, os 2 2 from django.conf import settings 3 from django.db import connection , get_creation_module3 from django.db import connection 4 4 from django.core import mail 5 from django.core.management import call_command6 5 from django.test import signals 7 6 from django.template import Template 8 7 from django.utils.translation import deactivate 9 8 10 # The prefix to put on the default database name when creating11 # the test database.12 TEST_DATABASE_PREFIX = 'test_'13 14 9 def instrumented_test_render(self, context): 15 10 """ 16 11 An instrumented Template render method, providing a signal … … 70 65 71 66 del mail.outbox 72 67 73 def _set_autocommit(connection):74 "Make sure a connection is in autocommit mode."75 if hasattr(connection.connection, "autocommit"):76 if callable(connection.connection.autocommit):77 connection.connection.autocommit(True)78 else:79 connection.connection.autocommit = True80 elif hasattr(connection.connection, "set_isolation_level"):81 connection.connection.set_isolation_level(0)82 83 def get_mysql_create_suffix():84 suffix = []85 if settings.TEST_DATABASE_CHARSET:86 suffix.append('CHARACTER SET %s' % settings.TEST_DATABASE_CHARSET)87 if settings.TEST_DATABASE_COLLATION:88 suffix.append('COLLATE %s' % settings.TEST_DATABASE_COLLATION)89 return ' '.join(suffix)90 91 def get_postgresql_create_suffix():92 assert settings.TEST_DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time."93 if settings.TEST_DATABASE_CHARSET:94 return "WITH ENCODING '%s'" % settings.TEST_DATABASE_CHARSET95 return ''96 97 def create_test_db(verbosity=1, autoclobber=False):98 """99 Creates a test database, prompting the user for confirmation if the100 database already exists. Returns the name of the test database created.101 """102 # If the database backend wants to create the test DB itself, let it103 creation_module = get_creation_module()104 if hasattr(creation_module, "create_test_db"):105 creation_module.create_test_db(settings, connection, verbosity, autoclobber)106 return107 108 if verbosity >= 1:109 print "Creating test database..."110 # If we're using SQLite, it's more convenient to test against an111 # in-memory database. Using the TEST_DATABASE_NAME setting you can still choose112 # to run on a physical database.113 if settings.DATABASE_ENGINE == "sqlite3":114 if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:":115 TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME116 # Erase the old test database117 if verbosity >= 1:118 print "Destroying old test database..."119 if os.access(TEST_DATABASE_NAME, os.F_OK):120 if not autoclobber:121 confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)122 if autoclobber or confirm == 'yes':123 try:124 if verbosity >= 1:125 print "Destroying old test database..."126 os.remove(TEST_DATABASE_NAME)127 except Exception, e:128 sys.stderr.write("Got an error deleting the old test database: %s\n" % e)129 sys.exit(2)130 else:131 print "Tests cancelled."132 sys.exit(1)133 if verbosity >= 1:134 print "Creating test database..."135 else:136 TEST_DATABASE_NAME = ":memory:"137 else:138 suffix = {139 'postgresql': get_postgresql_create_suffix,140 'postgresql_psycopg2': get_postgresql_create_suffix,141 'mysql': get_mysql_create_suffix,142 }.get(settings.DATABASE_ENGINE, lambda: '')()143 if settings.TEST_DATABASE_NAME:144 TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME145 else:146 TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME147 148 qn = connection.ops.quote_name149 150 # Create the test database and connect to it. We need to autocommit151 # if the database supports it because PostgreSQL doesn't allow152 # CREATE/DROP DATABASE statements within transactions.153 cursor = connection.cursor()154 _set_autocommit(connection)155 try:156 cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix))157 except Exception, e:158 sys.stderr.write("Got an error creating the test database: %s\n" % e)159 if not autoclobber:160 confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)161 if autoclobber or confirm == 'yes':162 try:163 if verbosity >= 1:164 print "Destroying old test database..."165 cursor.execute("DROP DATABASE %s" % qn(TEST_DATABASE_NAME))166 if verbosity >= 1:167 print "Creating test database..."168 cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix))169 except Exception, e:170 sys.stderr.write("Got an error recreating the test database: %s\n" % e)171 sys.exit(2)172 else:173 print "Tests cancelled."174 sys.exit(1)175 176 connection.close()177 settings.DATABASE_NAME = TEST_DATABASE_NAME178 179 call_command('syncdb', verbosity=verbosity, interactive=False)180 181 if settings.CACHE_BACKEND.startswith('db://'):182 cache_name = settings.CACHE_BACKEND[len('db://'):]183 call_command('createcachetable', cache_name)184 185 # Get a cursor (even though we don't need one yet). This has186 # the side effect of initializing the test database.187 cursor = connection.cursor()188 189 return TEST_DATABASE_NAME190 191 def destroy_test_db(old_database_name, verbosity=1):192 # If the database wants to drop the test DB itself, let it193 creation_module = get_creation_module()194 if hasattr(creation_module, "destroy_test_db"):195 creation_module.destroy_test_db(settings, connection, old_database_name, verbosity)196 return197 198 if verbosity >= 1:199 print "Destroying test database..."200 connection.close()201 TEST_DATABASE_NAME = settings.DATABASE_NAME202 settings.DATABASE_NAME = old_database_name203 if settings.DATABASE_ENGINE == "sqlite3":204 if TEST_DATABASE_NAME and TEST_DATABASE_NAME != ":memory:":205 # Remove the SQLite database file206 os.remove(TEST_DATABASE_NAME)207 else:208 # Remove the test database to clean up after209 # ourselves. Connect to the previous database (not the test database)210 # to do so, because it's not allowed to delete a database while being211 # connected to it.212 cursor = connection.cursor()213 _set_autocommit(connection)214 time.sleep(1) # To avoid "database is being accessed by other users" errors.215 cursor.execute("DROP DATABASE %s" % connection.ops.quote_name(TEST_DATABASE_NAME))216 connection.close() -
django/db/models/fields/__init__.py
old new 7 7 except ImportError: 8 8 from django.utils import _decimal as decimal # for Python 2.3 9 9 10 from django.db import connection , get_creation_module10 from django.db import connection 11 11 from django.db.models import signals 12 12 from django.db.models.query_utils import QueryWrapper 13 13 from django.dispatch import dispatcher … … 145 145 # as the TextField Django field type, which means XMLField's 146 146 # get_internal_type() returns 'TextField'. 147 147 # 148 # But the limitation of the get_internal_type() / DATA_TYPESapproach148 # But the limitation of the get_internal_type() / data_types approach 149 149 # is that it cannot handle database column types that aren't already 150 150 # mapped to one of the built-in Django field types. In this case, you 151 151 # can implement db_type() instead of get_internal_type() to specify 152 152 # exactly which wacky database column type you want to use. 153 153 data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_") 154 154 try: 155 return get_creation_module().DATA_TYPES[self.get_internal_type()] % data155 return connection.creation.data_types[self.get_internal_type()] % data 156 156 except KeyError: 157 157 return None 158 158 -
django/db/__init__.py
old new 14 14 # backends that ships with Django, so look there first. 15 15 _import_path = 'django.db.backends.' 16 16 backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, ['']) 17 creation = __import__('%s%s.creation' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])18 17 except ImportError, e: 19 18 # If the import failed, we might be looking for a database backend 20 19 # distributed external to Django. So we'll try that next. 21 20 try: 22 21 _import_path = '' 23 22 backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, ['']) 24 creation = __import__('%s.creation' % settings.DATABASE_ENGINE, {}, {}, [''])25 23 except ImportError, e_user: 26 24 # The database backend wasn't found. Display a helpful error message 27 25 # listing all possible (built-in) database backends. … … 29 27 available_backends = [f for f in os.listdir(backend_dir) if not f.startswith('_') and not f.startswith('.') and not f.endswith('.py') and not f.endswith('.pyc')] 30 28 available_backends.sort() 31 29 if settings.DATABASE_ENGINE not in available_backends: 32 raise ImproperlyConfigured, "%r isn't an available database backend. Available options are: %s " % \33 (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends )))30 raise ImproperlyConfigured, "%r isn't an available database backend. Available options are: %s\nError was: %s" % \ 31 (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends, e_user))) 34 32 else: 35 33 raise # If there's some other error, this must be an error in Django itself. 36 34 37 def _import_database_module(import_path='', module_name=''):38 """Lazily import a database module when requested."""39 return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])40 41 # We don't want to import the introspect module unless someone asks for it, so42 # lazily load it on demmand.43 get_introspection_module = curry(_import_database_module, _import_path, 'introspection')44 45 def get_creation_module():46 return creation47 48 # We want runshell() to work the same way, but we have to treat it a49 # little differently (since it just runs instead of returning a module like50 # the above) and wrap the lazily-loaded runshell() method.51 runshell = lambda: _import_database_module(_import_path, "client").runshell()52 53 35 # Convenient aliases for backend bits. 54 36 connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS) 55 37 DatabaseError = backend.DatabaseError -
django/db/backends/postgresql/base.py
old new 4 4 Requires psycopg 1: http://initd.org/projects/psycopg1 5 5 """ 6 6 7 from django.db.backends import BaseDatabaseFeatures 8 from django.db.backends import BaseDatabaseValidation 9 from django.db.backends import BaseDatabaseWrapper 10 from django.db.backends import util 11 from django.db.backends.postgresql.client import DatabaseClient 12 from django.db.backends.postgresql.creation import DatabaseCreation 13 from django.db.backends.postgresql.introspection import DatabaseIntrospection 14 from django.db.backends.postgresql.operations import DatabaseOperations 7 15 from django.utils.encoding import smart_str, smart_unicode 8 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, util 9 from django.db.backends.postgresql.operations import DatabaseOperations 16 10 17 try: 11 18 import psycopg as Database 12 19 except ImportError, e: … … 62 69 class DatabaseFeatures(BaseDatabaseFeatures): 63 70 pass # This backend uses all the defaults. 64 71 72 class DatabaseValidation(BaseDatabaseValidation): 73 pass 74 65 75 class DatabaseWrapper(BaseDatabaseWrapper): 66 features = DatabaseFeatures()67 ops = DatabaseOperations()68 76 operators = { 69 77 'exact': '= %s', 70 78 'iexact': 'ILIKE %s', … … 82 90 'iendswith': 'ILIKE %s', 83 91 } 84 92 93 def __init__(self, *args, **kwargs): 94 super(DatabaseWrapper, self).__init__(*args, **kwargs) 95 96 self.features = DatabaseFeatures() 97 self.ops = DatabaseOperations() 98 self.client = DatabaseClient() 99 self.creation = DatabaseCreation(self) 100 self.introspection = DatabaseIntrospection(self) 101 self.validation = DatabaseValidation() 102 85 103 def _cursor(self, settings): 86 104 set_tz = False 87 105 if self.connection is None: -
django/db/backends/postgresql/client.py
old new 1 from django.db.backends import BaseDatabaseClient 1 2 from django.conf import settings 2 3 import os 3 4 4 def runshell(): 5 args = ['psql'] 6 if settings.DATABASE_USER: 7 args += ["-U", settings.DATABASE_USER] 8 if settings.DATABASE_PASSWORD: 9 args += ["-W"] 10 if settings.DATABASE_HOST: 11 args.extend(["-h", settings.DATABASE_HOST]) 12 if settings.DATABASE_PORT: 13 args.extend(["-p", str(settings.DATABASE_PORT)]) 14 args += [settings.DATABASE_NAME] 15 os.execvp('psql', args) 5 class DatabaseClient(BaseDatabaseClient): 6 def runshell(self): 7 args = ['psql'] 8 if settings.DATABASE_USER: 9 args += ["-U", settings.DATABASE_USER] 10 if settings.DATABASE_PASSWORD: 11 args += ["-W"] 12 if settings.DATABASE_HOST: 13 args.extend(["-h", settings.DATABASE_HOST]) 14 if settings.DATABASE_PORT: 15 args.extend(["-p", str(settings.DATABASE_PORT)]) 16 args += [settings.DATABASE_NAME] 17 os.execvp('psql', args) -
django/db/backends/postgresql/introspection.py
old new 1 from django.db.backends .postgresql.base import DatabaseOperations1 from django.db.backends import BaseDatabaseIntrospection 2 2 3 quote_name = DatabaseOperations().quote_name 3 class DatabaseIntrospection(BaseDatabaseIntrospection): 4 # Maps type codes to Django Field types. 5 data_types_reverse = { 6 16: 'BooleanField', 7 21: 'SmallIntegerField', 8 23: 'IntegerField', 9 25: 'TextField', 10 701: 'FloatField', 11 869: 'IPAddressField', 12 1043: 'CharField', 13 1082: 'DateField', 14 1083: 'TimeField', 15 1114: 'DateTimeField', 16 1184: 'DateTimeField', 17 1266: 'TimeField', 18 1700: 'DecimalField', 19 } 20 21 def get_table_list(self, cursor): 22 "Returns a list of table names in the current database." 23 cursor.execute(""" 24 SELECT c.relname 25 FROM pg_catalog.pg_class c 26 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace 27 WHERE c.relkind IN ('r', 'v', '') 28 AND n.nspname NOT IN ('pg_catalog', 'pg_toast') 29 AND pg_catalog.pg_table_is_visible(c.oid)""") 30 return [row[0] for row in cursor.fetchall()] 4 31 5 def get_table_list(cursor): 6 "Returns a list of table names in the current database." 7 cursor.execute(""" 8 SELECT c.relname 9 FROM pg_catalog.pg_class c 10 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace 11 WHERE c.relkind IN ('r', 'v', '') 12 AND n.nspname NOT IN ('pg_catalog', 'pg_toast') 13 AND pg_catalog.pg_table_is_visible(c.oid)""") 14 return [row[0] for row in cursor.fetchall()] 32 def get_table_description(self, cursor, table_name): 33 "Returns a description of the table, with the DB-API cursor.description interface." 34 cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)) 35 return cursor.description 15 36 16 def get_table_description(cursor, table_name): 17 "Returns a description of the table, with the DB-API cursor.description interface." 18 cursor.execute("SELECT * FROM %s LIMIT 1" % quote_name(table_name)) 19 return cursor.description 37 def get_relations(self, cursor, table_name): 38 """ 39 Returns a dictionary of {field_index: (field_index_other_table, other_table)} 40 representing all relationships to the given table. Indexes are 0-based. 41 """ 42 cursor.execute(""" 43 SELECT con.conkey, con.confkey, c2.relname 44 FROM pg_constraint con, pg_class c1, pg_class c2 45 WHERE c1.oid = con.conrelid 46 AND c2.oid = con.confrelid 47 AND c1.relname = %s 48 AND con.contype = 'f'""", [table_name]) 49 relations = {} 50 for row in cursor.fetchall(): 51 try: 52 # row[0] and row[1] are like "{2}", so strip the curly braces. 53 relations[int(row[0][1:-1]) - 1] = (int(row[1][1:-1]) - 1, row[2]) 54 except ValueError: 55 continue 56 return relations 20 57 21 def get_relations(cursor, table_name): 22 """ 23 Returns a dictionary of {field_index: (field_index_other_table, other_table)} 24 representing all relationships to the given table. Indexes are 0-based. 25 """ 26 cursor.execute(""" 27 SELECT con.conkey, con.confkey, c2.relname 28 FROM pg_constraint con, pg_class c1, pg_class c2 29 WHERE c1.oid = con.conrelid 30 AND c2.oid = con.confrelid 31 AND c1.relname = %s 32 AND con.contype = 'f'""", [table_name]) 33 relations = {} 34 for row in cursor.fetchall(): 35 try: 36 # row[0] and row[1] are like "{2}", so strip the curly braces. 37 relations[int(row[0][1:-1]) - 1] = (int(row[1][1:-1]) - 1, row[2]) 38 except ValueError: 39 continue 40 return relations 58 def get_indexes(self, cursor, table_name): 59 """ 60 Returns a dictionary of fieldname -> infodict for the given table, 61 where each infodict is in the format: 62 {'primary_key': boolean representing whether it's the primary key, 63 'unique': boolean representing whether it's a unique index} 64 """ 65 # This query retrieves each index on the given table, including the 66 # first associated field name 67 cursor.execute(""" 68 SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary 69 FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, 70 pg_catalog.pg_index idx, pg_catalog.pg_attribute attr 71 WHERE c.oid = idx.indrelid 72 AND idx.indexrelid = c2.oid 73 AND attr.attrelid = c.oid 74 AND attr.attnum = idx.indkey[0] 75 AND c.relname = %s""", [table_name]) 76 indexes = {} 77 for row in cursor.fetchall(): 78 # row[1] (idx.indkey) is stored in the DB as an array. It comes out as 79 # a string of space-separated integers. This designates the field 80 # indexes (1-based) of the fields that have indexes on the table. 81 # Here, we skip any indexes across multiple fields. 82 if ' ' in row[1]: 83 continue 84 indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]} 85 return indexes 41 86 42 def get_indexes(cursor, table_name):43 """44 Returns a dictionary of fieldname -> infodict for the given table,45 where each infodict is in the format:46 {'primary_key': boolean representing whether it's the primary key,47 'unique': boolean representing whether it's a unique index}48 """49 # This query retrieves each index on the given table, including the50 # first associated field name51 cursor.execute("""52 SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary53 FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,54 pg_catalog.pg_index idx, pg_catalog.pg_attribute attr55 WHERE c.oid = idx.indrelid56 AND idx.indexrelid = c2.oid57 AND attr.attrelid = c.oid58 AND attr.attnum = idx.indkey[0]59 AND c.relname = %s""", [table_name])60 indexes = {}61 for row in cursor.fetchall():62 # row[1] (idx.indkey) is stored in the DB as an array. It comes out as63 # a string of space-separated integers. This designates the field64 # indexes (1-based) of the fields that have indexes on the table.65 # Here, we skip any indexes across multiple fields.66 if ' ' in row[1]:67 continue68 indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}69 return indexes70 71 # Maps type codes to Django Field types.72 DATA_TYPES_REVERSE = {73 16: 'BooleanField',74 21: 'SmallIntegerField',75 23: 'IntegerField',76 25: 'TextField',77 701: 'FloatField',78 869: 'IPAddressField',79 1043: 'CharField',80 1082: 'DateField',81 1083: 'TimeField',82 1114: 'DateTimeField',83 1184: 'DateTimeField',84 1266: 'TimeField',85 1700: 'DecimalField',86 } -
django/db/backends/postgresql/creation.py
old new 1 # This dictionary maps Field objects to their associated PostgreSQL column 2 # types, as strings. Column-type strings can contain format strings; they'll 3 # be interpolated against the values of Field.__dict__ before being output. 4 # If a column type is set to None, it won't be included in the output. 5 DATA_TYPES = { 6 'AutoField': 'serial', 7 'BooleanField': 'boolean', 8 'CharField': 'varchar(%(max_length)s)', 9 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 10 'DateField': 'date', 11 'DateTimeField': 'timestamp with time zone', 12 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 13 'FileField': 'varchar(%(max_length)s)', 14 'FilePathField': 'varchar(%(max_length)s)', 15 'FloatField': 'double precision', 16 'IntegerField': 'integer', 17 'IPAddressField': 'inet', 18 'NullBooleanField': 'boolean', 19 'OneToOneField': 'integer', 20 'PhoneNumberField': 'varchar(20)', 21 'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)', 22 'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)', 23 'SlugField': 'varchar(%(max_length)s)', 24 'SmallIntegerField': 'smallint', 25 'TextField': 'text', 26 'TimeField': 'time', 27 'USStateField': 'varchar(2)', 28 } 1 from django.conf import settings 2 from django.db.backends.creation import BaseDatabaseCreation 3 4 class DatabaseCreation(BaseDatabaseCreation): 5 # This dictionary maps Field objects to their associated PostgreSQL column 6 # types, as strings. Column-type strings can contain format strings; they'll 7 # be interpolated against the values of Field.__dict__ before being output. 8 # If a column type is set to None, it won't be included in the output. 9 data_types = { 10 'AutoField': 'serial', 11 'BooleanField': 'boolean', 12 'CharField': 'varchar(%(max_length)s)', 13 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 14 'DateField': 'date', 15 'DateTimeField': 'timestamp with time zone', 16 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 17 'FileField': 'varchar(%(max_length)s)', 18 'FilePathField': 'varchar(%(max_length)s)', 19 'FloatField': 'double precision', 20 'IntegerField': 'integer', 21 'IPAddressField': 'inet', 22 'NullBooleanField': 'boolean', 23 'OneToOneField': 'integer', 24 'PhoneNumberField': 'varchar(20)', 25 'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)', 26 'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)', 27 'SlugField': 'varchar(%(max_length)s)', 28 'SmallIntegerField': 'smallint', 29 'TextField': 'text', 30 'TimeField': 'time', 31 'USStateField': 'varchar(2)', 32 } 33 34 def _creation_suffix(self): 35 assert settings.TEST_DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time." 36 if settings.TEST_DATABASE_CHARSET: 37 return "WITH ENCODING '%s'" % settings.TEST_DATABASE_CHARSET 38 return '' -
django/db/backends/sqlite3/base.py
old new 6 6 Python 2.5 and later use the sqlite3 module in the standard library. 7 7 """ 8 8 9 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util 9 from django.db.backends import BaseDatabaseWrapper 10 from django.db.backends import BaseDatabaseFeatures 11 from django.db.backends import BaseDatabaseOperations 12 from django.db.backends import BaseDatabaseValidation 13 from django.db.backends import util 14 from django.db.backends.sqlite3.client import DatabaseClient 15 from django.db.backends.sqlite3.creation import DatabaseCreation 16 from django.db.backends.sqlite3.introspection import DatabaseIntrospection 17 10 18 try: 11 19 try: 12 20 from sqlite3 import dbapi2 as Database … … 39 47 Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal) 40 48 41 49 class DatabaseFeatures(BaseDatabaseFeatures): 42 supports_constraints = False43 50 # SQLite cannot handle us only partially reading from a cursor's result set 44 51 # and then writing the same rows to the database in another cursor. This 45 52 # setting ensures we always read result sets fully into memory all in one … … 89 96 second = '%s-12-31 23:59:59.999999' 90 97 return [first % value, second % value] 91 98 99 class DatabaseValidation(BaseDatabaseValidation): 100 pass 92 101 93 102 class DatabaseWrapper(BaseDatabaseWrapper): 94 features = DatabaseFeatures() 95 ops = DatabaseOperations() 96 103 97 104 # SQLite requires LIKE statements to include an ESCAPE clause if the value 98 105 # being escaped has a percent or underscore in it. 99 106 # See http://www.sqlite.org/lang_expr.html for an explanation. … … 114 121 'iendswith': "LIKE %s ESCAPE '\\'", 115 122 } 116 123 124 def __init__(self, *args, **kwargs): 125 super(DatabaseWrapper, self).__init__(*args, **kwargs) 126 127 self.features = DatabaseFeatures() 128 self.ops = DatabaseOperations() 129 self.client = DatabaseClient() 130 self.creation = DatabaseCreation(self) 131 self.introspection = DatabaseIntrospection(self) 132 self.validation = DatabaseValidation() 133 117 134 def _cursor(self, settings): 118 135 if self.connection is None: 119 136 if not settings.DATABASE_NAME: -
django/db/backends/sqlite3/client.py
old new 1 from django.db.backends import BaseDatabaseClient 1 2 from django.conf import settings 2 3 import os 3 4 4 def runshell(): 5 args = ['', settings.DATABASE_NAME] 6 os.execvp('sqlite3', args) 5 class DatabaseClient(BaseDatabaseClient): 6 def runshell(self): 7 args = ['', settings.DATABASE_NAME] 8 os.execvp('sqlite3', args) -
django/db/backends/sqlite3/introspection.py
old new 1 from django.db.backends .sqlite3.base import DatabaseOperations1 from django.db.backends import BaseDatabaseIntrospection 2 2 3 quote_name = DatabaseOperations().quote_name4 5 def get_table_list(cursor):6 "Returns a list of table names in the current database."7 # Skip the sqlite_sequence system table used for autoincrement key8 # generation.9 cursor.execute("""10 SELECT name FROM sqlite_master11 WHERE type='table' AND NOT name='sqlite_sequence'12 ORDER BY name""")13 return [row[0] for row in cursor.fetchall()]14 15 def get_table_description(cursor, table_name):16 "Returns a description of the table, with the DB-API cursor.description interface."17 return [(info['name'], info['type'], None, None, None, None,18 info['null_ok']) for info in _table_info(cursor, table_name)]19 20 def get_relations(cursor, table_name):21 raise NotImplementedError22 23 def get_indexes(cursor, table_name):24 """25 Returns a dictionary of fieldname -> infodict for the given table,26 where each infodict is in the format:27 {'primary_key': boolean representing whether it's the primary key,28 'unique': boolean representing whether it's a unique index}29 """30 indexes = {}31 for info in _table_info(cursor, table_name):32 indexes[info['name']] = {'primary_key': info['pk'] != 0,33 'unique': False}34 cursor.execute('PRAGMA index_list(%s)' % quote_name(table_name))35 # seq, name, unique36 for index, unique in [(field[1], field[2]) for field in cursor.fetchall()]:37 if not unique:38 continue39 cursor.execute('PRAGMA index_info(%s)' % quote_name(index))40 info = cursor.fetchall()41 # Skip indexes across multiple fields42 if len(info) != 1:43 continue44 name = info[0][2] # seqno, cid, name45 indexes[name]['unique'] = True46 return indexes47 48 def _table_info(cursor, name):49 cursor.execute('PRAGMA table_info(%s)' % quote_name(name))50 # cid, name, type, notnull, dflt_value, pk51 return [{'name': field[1],52 'type': field[2],53 'null_ok': not field[3],54 'pk': field[5] # undocumented55 } for field in cursor.fetchall()]56 57 # Maps SQL types to Django Field types. Some of the SQL types have multiple58 # entries here because SQLite allows for anything and doesn't normalize the59 # field type; it uses whatever was given.60 BASE_DATA_TYPES_REVERSE = {61 'bool': 'BooleanField',62 'boolean': 'BooleanField',63 'smallint': 'SmallIntegerField',64 'smallinteger': 'SmallIntegerField',65 'int': 'IntegerField',66 'integer': 'IntegerField',67 'text': 'TextField',68 'char': 'CharField',69 'date': 'DateField',70 'datetime': 'DateTimeField',71 'time': 'TimeField',72 }73 74 3 # This light wrapper "fakes" a dictionary interface, because some SQLite data 75 4 # types include variables in them -- e.g. "varchar(30)" -- and can't be matched 76 5 # as a simple dictionary lookup. 77 6 class FlexibleFieldLookupDict: 7 # Maps SQL types to Django Field types. Some of the SQL types have multiple 8 # entries here because SQLite allows for anything and doesn't normalize the 9 # field type; it uses whatever was given. 10 base_data_types_reverse = { 11 'bool': 'BooleanField', 12 'boolean': 'BooleanField', 13 'smallint': 'SmallIntegerField', 14 'smallinteger': 'SmallIntegerField', 15 'int': 'IntegerField', 16 'integer': 'IntegerField', 17 'text': 'TextField', 18 'char': 'CharField', 19 'date': 'DateField', 20 'datetime': 'DateTimeField', 21 'time': 'TimeField', 22 } 23 78 24 def __getitem__(self, key): 79 25 key = key.lower() 80 26 try: 81 return BASE_DATA_TYPES_REVERSE[key]27 return self.base_data_types_reverse[key] 82 28 except KeyError: 83 29 import re 84 30 m = re.search(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$', key) … … 86 32 return ('CharField', {'max_length': int(m.group(1))}) 87 33 raise KeyError 88 34 89 DATA_TYPES_REVERSE = FlexibleFieldLookupDict() 35 class DatabaseIntrospection(BaseDatabaseIntrospection): 36 data_types_reverse = FlexibleFieldLookupDict() 37 38 def get_table_list(self, cursor): 39 "Returns a list of table names in the current database." 40 # Skip the sqlite_sequence system table used for autoincrement key 41 # generation. 42 cursor.execute(""" 43 SELECT name FROM sqlite_master 44 WHERE type='table' AND NOT name='sqlite_sequence' 45 ORDER BY name""") 46 return [row[0] for row in cursor.fetchall()] 47 48 def get_table_description(self, cursor, table_name): 49 "Returns a description of the table, with the DB-API cursor.description interface." 50 return [(info['name'], info['type'], None, None, None, None, 51 info['null_ok']) for info in self._table_info(cursor, table_name)] 52 53 def get_relations(self, cursor, table_name): 54 raise NotImplementedError 55 56 def get_indexes(self, cursor, table_name): 57 """ 58 Returns a dictionary of fieldname -> infodict for the given table, 59 where each infodict is in the format: 60 {'primary_key': boolean representing whether it's the primary key, 61 'unique': boolean representing whether it's a unique index} 62 """ 63 indexes = {} 64 for info in self._table_info(cursor, table_name): 65 indexes[info['name']] = {'primary_key': info['pk'] != 0, 66 'unique': False} 67 cursor.execute('PRAGMA index_list(%s)' % self.ops.quote_name(table_name)) 68 # seq, name, unique 69 for index, unique in [(field[1], field[2]) for field in cursor.fetchall()]: 70 if not unique: 71 continue 72 cursor.execute('PRAGMA index_info(%s)' % self.ops.quote_name(index)) 73 info = cursor.fetchall() 74 # Skip indexes across multiple fields 75 if len(info) != 1: 76 continue 77 name = info[0][2] # seqno, cid, name 78 indexes[name]['unique'] = True 79 return indexes 80 81 def _table_info(self, cursor, name): 82 cursor.execute('PRAGMA table_info(%s)' % self.ops.quote_name(name)) 83 # cid, name, type, notnull, dflt_value, pk 84 return [{'name': field[1], 85 'type': field[2], 86 'null_ok': not field[3], 87 'pk': field[5] # undocumented 88 } for field in cursor.fetchall()] 89 -
django/db/backends/sqlite3/creation.py
old new 1 # SQLite doesn't actually support most of these types, but it "does the right 2 # thing" given more verbose field definitions, so leave them as is so that 3 # schema inspection is more useful. 4 DATA_TYPES = { 5 'AutoField': 'integer', 6 'BooleanField': 'bool', 7 'CharField': 'varchar(%(max_length)s)', 8 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 9 'DateField': 'date', 10 'DateTimeField': 'datetime', 11 'DecimalField': 'decimal', 12 'FileField': 'varchar(%(max_length)s)', 13 'FilePathField': 'varchar(%(max_length)s)', 14 'FloatField': 'real', 15 'IntegerField': 'integer', 16 'IPAddressField': 'char(15)', 17 'NullBooleanField': 'bool', 18 'OneToOneField': 'integer', 19 'PhoneNumberField': 'varchar(20)', 20 'PositiveIntegerField': 'integer unsigned', 21 'PositiveSmallIntegerField': 'smallint unsigned', 22 'SlugField': 'varchar(%(max_length)s)', 23 'SmallIntegerField': 'smallint', 24 'TextField': 'text', 25 'TimeField': 'time', 26 'USStateField': 'varchar(2)', 27 } 1 import os 2 import sys 3 from django.conf import settings 4 from django.db.backends.creation import BaseDatabaseCreation 5 6 class DatabaseCreation(BaseDatabaseCreation): 7 # SQLite doesn't actually support most of these types, but it "does the right 8 # thing" given more verbose field definitions, so leave them as is so that 9 # schema inspection is more useful. 10 data_types = { 11 'AutoField': 'integer', 12 'BooleanField': 'bool', 13 'CharField': 'varchar(%(max_length)s)', 14 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 15 'DateField': 'date', 16 'DateTimeField': 'datetime', 17 'DecimalField': 'decimal', 18 'FileField': 'varchar(%(max_length)s)', 19 'FilePathField': 'varchar(%(max_length)s)', 20 'FloatField': 'real', 21 'IntegerField': 'integer', 22 'IPAddressField': 'char(15)', 23 'NullBooleanField': 'bool', 24 'OneToOneField': 'integer', 25 'PhoneNumberField': 'varchar(20)', 26 'PositiveIntegerField': 'integer unsigned', 27 'PositiveSmallIntegerField': 'smallint unsigned', 28 'SlugField': 'varchar(%(max_length)s)', 29 'SmallIntegerField': 'smallint', 30 'TextField': 'text', 31 'TimeField': 'time', 32 'USStateField': 'varchar(2)', 33 } 34 35 def sql_for_pending_references(self, model, style, pending_references): 36 "SQLite3 doesn't support constraints" 37 return [] 38 39 def sql_remove_table_constraints(self, model, references_to_delete): 40 "SQLite3 doesn't support constraints" 41 return [] 42 43 def _create_test_db(self, verbosity, autoclobber): 44 if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:": 45 test_database_name = settings.TEST_DATABASE_NAME 46 # Erase the old test database 47 if verbosity >= 1: 48 print "Destroying old test database..." 49 if os.access(test_database_name, os.F_OK): 50 if not autoclobber: 51 confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % test_database_name) 52 if autoclobber or confirm == 'yes': 53 try: 54 if verbosity >= 1: 55 print "Destroying old test database..." 56 os.remove(test_database_name) 57 except Exception, e: 58 sys.stderr.write("Got an error deleting the old test database: %s\n" % e) 59 sys.exit(2) 60 else: 61 print "Tests cancelled." 62 sys.exit(1) 63 if verbosity >= 1: 64 print "Creating test database..." 65 else: 66 test_database_name = ":memory:" 67 return test_database_name 68 69 def _destroy_test_db(self, test_database_name, verbosity): 70 if test_database_name and test_database_name != ":memory:": 71 # Remove the SQLite database file 72 os.remove(test_database_name) 73 -
django/db/backends/mysql/base.py
old new 4 4 Requires MySQLdb: http://sourceforge.net/projects/mysql-python 5 5 """ 6 6 7 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util 7 from django.db.backends import BaseDatabaseFeatures 8 from django.db.backends import BaseDatabaseOperations 9 from django.db.backends import BaseDatabaseWrapper 10 from django.db.backends import util 11 from django.db.backends.mysql.client import DatabaseClient 12 from django.db.backends.mysql.creation import DatabaseCreation &nbs
