Django

Code

Ticket #5461: 5461-r8194.diff

File 5461-r8194.diff, 142.7 kB (added by russellm, 5 months ago)

More refactored database backends - all SQL statements now behind backend interface.

  • django/test/simple.py

    old new  
    33from django.db.models import get_app, get_apps 
    44from django.test import _doctest as doctest 
    55from django.test.utils import setup_test_environment, teardown_test_environment 
    6 from django.test.utils import create_test_db, destroy_test_db 
    76from django.test.testcases import OutputChecker, DocTestRunner 
    87 
    98# The module name for tests outside models.py 
     
    139138        suite.addTest(test) 
    140139 
    141140    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) 
    143143    result = unittest.TextTestRunner(verbosity=verbosity).run(suite) 
    144     destroy_test_db(old_name, verbosity) 
     144    connection.creation.destroy_test_db(old_name, verbosity) 
    145145     
    146146    teardown_test_environment() 
    147147     
  • django/test/utils.py

    old new  
    11import sys, time, os 
    22from django.conf import settings 
    3 from django.db import connection, get_creation_module 
     3from django.db import connection 
    44from django.core import mail 
    5 from django.core.management import call_command 
    65from django.dispatch import dispatcher 
    76from django.test import signals 
    87from django.template import Template 
    98from django.utils.translation import deactivate 
    109 
    11 # The prefix to put on the default database name when creating 
    12 # the test database. 
    13 TEST_DATABASE_PREFIX = 'test_' 
    14  
    1510def instrumented_test_render(self, context): 
    1611    """ 
    1712    An instrumented Template render method, providing a signal 
     
    7166 
    7267    del mail.outbox 
    7368 
    74 def _set_autocommit(connection): 
    75     "Make sure a connection is in autocommit mode." 
    76     if hasattr(connection.connection, "autocommit"): 
    77         if callable(connection.connection.autocommit): 
    78             connection.connection.autocommit(True) 
    79         else: 
    80             connection.connection.autocommit = True 
    81     elif hasattr(connection.connection, "set_isolation_level"): 
    82         connection.connection.set_isolation_level(0) 
    83  
    84 def get_mysql_create_suffix(): 
    85     suffix = [] 
    86     if settings.TEST_DATABASE_CHARSET: 
    87         suffix.append('CHARACTER SET %s' % settings.TEST_DATABASE_CHARSET) 
    88     if settings.TEST_DATABASE_COLLATION: 
    89         suffix.append('COLLATE %s' % settings.TEST_DATABASE_COLLATION) 
    90     return ' '.join(suffix) 
    91  
    92 def get_postgresql_create_suffix(): 
    93     assert settings.TEST_DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time." 
    94     if settings.TEST_DATABASE_CHARSET: 
    95         return "WITH ENCODING '%s'" % settings.TEST_DATABASE_CHARSET 
    96     return '' 
    97  
    98 def create_test_db(verbosity=1, autoclobber=False): 
    99     """ 
    100     Creates a test database, prompting the user for confirmation if the 
    101     database already exists. Returns the name of the test database created. 
    102     """ 
    103     # If the database backend wants to create the test DB itself, let it 
    104     creation_module = get_creation_module() 
    105     if hasattr(creation_module, "create_test_db"): 
    106         creation_module.create_test_db(settings, connection, verbosity, autoclobber) 
    107         return 
    108  
    109     if verbosity >= 1: 
    110         print "Creating test database..." 
    111     # If we're using SQLite, it's more convenient to test against an 
    112     # in-memory database. Using the TEST_DATABASE_NAME setting you can still choose 
    113     # to run on a physical database. 
    114     if settings.DATABASE_ENGINE == "sqlite3": 
    115         if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:": 
    116             TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME 
    117             # Erase the old test database 
    118             if verbosity >= 1: 
    119                 print "Destroying old test database..." 
    120             if os.access(TEST_DATABASE_NAME, os.F_OK): 
    121                 if not autoclobber: 
    122                     confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME) 
    123                 if autoclobber or confirm == 'yes': 
    124                   try: 
    125                       if verbosity >= 1: 
    126                           print "Destroying old test database..." 
    127                       os.remove(TEST_DATABASE_NAME) 
    128                   except Exception, e: 
    129                       sys.stderr.write("Got an error deleting the old test database: %s\n" % e) 
    130                       sys.exit(2) 
    131                 else: 
    132                     print "Tests cancelled." 
    133                     sys.exit(1) 
    134             if verbosity >= 1: 
    135                 print "Creating test database..." 
    136         else: 
    137             TEST_DATABASE_NAME = ":memory:" 
    138     else: 
    139         suffix = { 
    140             'postgresql': get_postgresql_create_suffix, 
    141             'postgresql_psycopg2': get_postgresql_create_suffix, 
    142             'mysql': get_mysql_create_suffix, 
    143         }.get(settings.DATABASE_ENGINE, lambda: '')() 
    144         if settings.TEST_DATABASE_NAME: 
    145             TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME 
    146         else: 
    147             TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME 
    148  
    149         qn = connection.ops.quote_name 
    150  
    151         # Create the test database and connect to it. We need to autocommit 
    152         # if the database supports it because PostgreSQL doesn't allow 
    153         # CREATE/DROP DATABASE statements within transactions. 
    154         cursor = connection.cursor() 
    155         _set_autocommit(connection) 
    156         try: 
    157             cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix)) 
    158         except Exception, e: 
    159             sys.stderr.write("Got an error creating the test database: %s\n" % e) 
    160             if not autoclobber: 
    161                 confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME) 
    162             if autoclobber or confirm == 'yes': 
    163                 try: 
    164                     if verbosity >= 1: 
    165                         print "Destroying old test database..." 
    166                     cursor.execute("DROP DATABASE %s" % qn(TEST_DATABASE_NAME)) 
    167                     if verbosity >= 1: 
    168                         print "Creating test database..." 
    169                     cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix)) 
    170                 except Exception, e: 
    171                     sys.stderr.write("Got an error recreating the test database: %s\n" % e) 
    172                     sys.exit(2) 
    173             else: 
    174                 print "Tests cancelled." 
    175                 sys.exit(1) 
    176  
    177     connection.close() 
    178     settings.DATABASE_NAME = TEST_DATABASE_NAME 
    179  
    180     call_command('syncdb', verbosity=verbosity, interactive=False) 
    181  
    182     if settings.CACHE_BACKEND.startswith('db://'): 
    183         cache_name = settings.CACHE_BACKEND[len('db://'):] 
    184         call_command('createcachetable', cache_name) 
    185  
    186     # Get a cursor (even though we don't need one yet). This has 
    187     # the side effect of initializing the test database. 
    188     cursor = connection.cursor() 
    189  
    190     return TEST_DATABASE_NAME 
    191  
    192 def destroy_test_db(old_database_name, verbosity=1): 
    193     # If the database wants to drop the test DB itself, let it 
    194     creation_module = get_creation_module() 
    195     if hasattr(creation_module, "destroy_test_db"): 
    196         creation_module.destroy_test_db(settings, connection, old_database_name, verbosity) 
    197         return 
    198  
    199     if verbosity >= 1: 
    200         print "Destroying test database..." 
    201     connection.close() 
    202     TEST_DATABASE_NAME = settings.DATABASE_NAME 
    203     settings.DATABASE_NAME = old_database_name 
    204     if settings.DATABASE_ENGINE == "sqlite3": 
    205         if TEST_DATABASE_NAME and TEST_DATABASE_NAME != ":memory:": 
    206             # Remove the SQLite database file 
    207             os.remove(TEST_DATABASE_NAME) 
    208     else: 
    209         # Remove the test database to clean up after 
    210         # ourselves. Connect to the previous database (not the test database) 
    211         # to do so, because it's not allowed to delete a database while being 
    212         # connected to it. 
    213         cursor = connection.cursor() 
    214         _set_autocommit(connection) 
    215         time.sleep(1) # To avoid "database is being accessed by other users" errors. 
    216         cursor.execute("DROP DATABASE %s" % connection.ops.quote_name(TEST_DATABASE_NAME)) 
    217         connection.close() 
  • django/db/models/fields/__init__.py

    old new  
    77except ImportError: 
    88    from django.utils import _decimal as decimal    # for Python 2.3 
    99 
    10 from django.db import connection, get_creation_module 
     10from django.db import connection 
    1111from django.db.models import signals 
    1212from django.db.models.query_utils import QueryWrapper 
    1313from django.dispatch import dispatcher 
     
    145145        # as the TextField Django field type, which means XMLField's 
    146146        # get_internal_type() returns 'TextField'. 
    147147        # 
    148         # But the limitation of the get_internal_type() / DATA_TYPES approach 
     148        # But the limitation of the get_internal_type() / data_types approach 
    149149        # is that it cannot handle database column types that aren't already 
    150150        # mapped to one of the built-in Django field types. In this case, you 
    151151        # can implement db_type() instead of get_internal_type() to specify 
    152152        # exactly which wacky database column type you want to use. 
    153153        data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_") 
    154154        try: 
    155             return get_creation_module().DATA_TYPES[self.get_internal_type()] % data 
     155            return connection.creation.data_types[self.get_internal_type()] % data 
    156156        except KeyError: 
    157157            return None 
    158158 
  • django/db/__init__.py

    old new  
    1515    # backends that ships with Django, so look there first. 
    1616    _import_path = 'django.db.backends.' 
    1717    backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, ['']) 
    18     creation = __import__('%s%s.creation' % (_import_path, settings.DATABASE_ENGINE), {}, {}, ['']) 
    1918except ImportError, e: 
     19    import traceback 
     20    traceback.print_exc() 
    2021    # If the import failed, we might be looking for a database backend 
    2122    # distributed external to Django. So we'll try that next. 
    2223    try: 
    2324        _import_path = '' 
    2425        backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, ['']) 
    25         creation = __import__('%s.creation' % settings.DATABASE_ENGINE, {}, {}, ['']) 
    2626    except ImportError, e_user: 
    2727        # The database backend wasn't found. Display a helpful error message 
    2828        # listing all possible (built-in) database backends. 
     
    3535        else: 
    3636            raise # If there's some other error, this must be an error in Django itself. 
    3737 
    38 def _import_database_module(import_path='', module_name=''): 
    39     """Lazily import a database module when requested.""" 
    40     return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, ['']) 
    41  
    42 # We don't want to import the introspect module unless someone asks for it, so 
    43 # lazily load it on demmand. 
    44 get_introspection_module = curry(_import_database_module, _import_path, 'introspection') 
    45  
    46 def get_creation_module(): 
    47     return creation 
    48  
    49 # We want runshell() to work the same way, but we have to treat it a 
    50 # little differently (since it just runs instead of returning a module like 
    51 # the above) and wrap the lazily-loaded runshell() method. 
    52 runshell = lambda: _import_database_module(_import_path, "client").runshell() 
    53  
    5438# Convenient aliases for backend bits. 
    5539connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS) 
    5640DatabaseError = backend.DatabaseError 
  • django/db/backends/postgresql/base.py

    old new  
    44Requires psycopg 1: http://initd.org/projects/psycopg1 
    55""" 
    66 
     7from django.db.backends import BaseDatabaseFeatures 
     8from django.db.backends import BaseDatabaseValidation 
     9from django.db.backends import BaseDatabaseWrapper 
     10from django.db.backends import util 
     11from django.db.backends.postgresql.client import DatabaseClient 
     12from django.db.backends.postgresql.creation import DatabaseCreation 
     13from django.db.backends.postgresql.introspection import DatabaseIntrospection 
     14from django.db.backends.postgresql.operations import DatabaseOperations 
    715from 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 
    1017try: 
    1118    import psycopg as Database 
    1219except ImportError, e: 
     
    6269class DatabaseFeatures(BaseDatabaseFeatures): 
    6370    pass # This backend uses all the defaults. 
    6471 
     72class DatabaseValidation(BaseDatabaseValidation): 
     73    pass 
     74 
    6575class DatabaseWrapper(BaseDatabaseWrapper): 
    6676    features = DatabaseFeatures() 
    6777    ops = DatabaseOperations() 
     78    client = DatabaseClient() 
     79    creation = DatabaseCreation(ops, features) 
     80    introspection = DatabaseIntrospection(ops) 
     81    validation = DatabaseValidation() 
     82 
    6883    operators = { 
    6984        'exact': '= %s', 
    7085        'iexact': 'ILIKE %s', 
  • django/db/backends/postgresql/client.py

    old new  
     1from django.db.backends import BaseDatabaseClient 
    12from django.conf import settings 
    23import os 
    34 
    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) 
     5class 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 DatabaseOperations 
     1from django.db.backends import BaseDatabaseIntrospection 
    22 
    3 quote_name = DatabaseOperations().quote_name 
     3class 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 __init__(self, ops): 
     22        self.ops = ops 
     23         
     24    def get_table_list(self, cursor): 
     25        "Returns a list of table names in the current database." 
     26        cursor.execute(""" 
     27            SELECT c.relname 
     28            FROM pg_catalog.pg_class c 
     29            LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace 
     30            WHERE c.relkind IN ('r', 'v', '') 
     31                AND n.nspname NOT IN ('pg_catalog', 'pg_toast') 
     32                AND pg_catalog.pg_table_is_visible(c.oid)""") 
     33        return [row[0] for row in cursor.fetchall()] 
    434 
    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()] 
     35    def get_table_description(self, cursor, table_name): 
     36        "Returns a description of the table, with the DB-API cursor.description interface." 
     37        cursor.execute("SELECT * FROM %s LIMIT 1" % self.ops.quote_name(table_name)) 
     38        return cursor.description 
    1539 
    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 
     40    def get_relations(self, cursor, table_name): 
     41        """ 
     42        Returns a dictionary of {field_index: (field_index_other_table, other_table)} 
     43        representing all relationships to the given table. Indexes are 0-based. 
     44        """ 
     45        cursor.execute(""" 
     46            SELECT con.conkey, con.confkey, c2.relname 
     47            FROM pg_constraint con, pg_class c1, pg_class c2 
     48            WHERE c1.oid = con.conrelid 
     49                AND c2.oid = con.confrelid 
     50                AND c1.relname = %s 
     51                AND con.contype = 'f'""", [table_name]) 
     52        relations = {} 
     53        for row in cursor.fetchall(): 
     54            try: 
     55                # row[0] and row[1] are like "{2}", so strip the curly braces. 
     56                relations[int(row[0][1:-1]) - 1] = (int(row[1][1:-1]) - 1, row[2]) 
     57            except ValueError: 
     58                continue 
     59        return relations 
    2060 
    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 
     61    def get_indexes(self, cursor, table_name): 
     62        """ 
     63        Returns a dictionary of fieldname -> infodict for the given table, 
     64        where each infodict is in the format: 
     65            {'primary_key': boolean representing whether it's the primary key, 
     66             'unique': boolean representing whether it's a unique index} 
     67        """ 
     68        # This query retrieves each index on the given table, including the 
     69        # first associated field name 
     70        cursor.execute(""" 
     71            SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary 
     72            FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, 
     73                pg_catalog.pg_index idx, pg_catalog.pg_attribute attr 
     74            WHERE c.oid = idx.indrelid 
     75                AND idx.indexrelid = c2.oid 
     76                AND attr.attrelid = c.oid 
     77                AND attr.attnum = idx.indkey[0] 
     78                AND c.relname = %s""", [table_name]) 
     79        indexes = {} 
     80        for row in cursor.fetchall(): 
     81            # row[1] (idx.indkey) is stored in the DB as an array. It comes out as 
     82            # a string of space-separated integers. This designates the field 
     83            # indexes (1-based) of the fields that have indexes on the table. 
     84            # Here, we skip any indexes across multiple fields. 
     85            if ' ' in row[1]: 
     86                continue 
     87            indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]} 
     88        return indexes 
    4189 
    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 the 
    50     # first associated field name 
    51     cursor.execute(""" 
    52         SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary 
    53         FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, 
    54             pg_catalog.pg_index idx, pg_catalog.pg_attribute attr 
    55         WHERE c.oid = idx.indrelid 
    56             AND idx.indexrelid = c2.oid 
    57             AND attr.attrelid = c.oid 
    58             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 as 
    63         # a string of space-separated integers. This designates the field 
    64         # 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             continue 
    68         indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]} 
    69     return indexes 
    70  
    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 
     1from django.conf import settings 
     2from django.db.backends.creation import BaseDatabaseCreation 
     3 
     4class 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  
    66Python 2.5 and later use the sqlite3 module in the standard library. 
    77""" 
    88 
    9 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util 
     9from django.db.backends import BaseDatabaseWrapper 
     10from django.db.backends import BaseDatabaseFeatures 
     11from django.db.backends import BaseDatabaseOperations 
     12from django.db.backends import BaseDatabaseValidation 
     13from django.db.backends import util 
     14from django.db.backends.sqlite3.client import DatabaseClient 
     15from django.db.backends.sqlite3.creation import DatabaseCreation 
     16from django.db.backends.sqlite3.introspection import DatabaseIntrospection 
     17 
    1018try: 
    1119    try: 
    1220        from sqlite3 import dbapi2 as Database 
     
    8997        second = '%s-12-31 23:59:59.999999' 
    9098        return [first % value, second % value] 
    9199 
     100class DatabaseValidation(BaseDatabaseValidation): 
     101    pass 
    92102 
    93103class DatabaseWrapper(BaseDatabaseWrapper): 
    94104    features = DatabaseFeatures() 
    95105    ops = DatabaseOperations() 
    96  
     106    client = DatabaseClient() 
     107    creation = DatabaseCreation(ops, features) 
     108    introspection = DatabaseIntrospection(ops) 
     109    validation = DatabaseValidation() 
     110     
    97111    # SQLite requires LIKE statements to include an ESCAPE clause if the value 
    98112    # being escaped has a percent or underscore in it. 
    99113    # See http://www.sqlite.org/lang_expr.html for an explanation. 
  • django/db/backends/sqlite3/client.py

    old new  
     1from django.db.backends import BaseDatabaseClient 
    12from django.conf import settings 
    23import os 
    34 
    4 def runshell(): 
    5     args = ['', settings.DATABASE_NAME] 
    6     os.execvp('sqlite3', args) 
     5class 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 DatabaseOperations 
     1from django.db.backends import BaseDatabaseIntrospection 
    22 
    3 quote_name = DatabaseOperations().quote_name 
    4  
    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 key 
    8     # generation. 
    9     cursor.execute(""" 
    10         SELECT name FROM sqlite_master 
    11         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 NotImplementedError 
    22  
    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, unique 
    36     for index, unique in [(field[1], field[2]) for field in cursor.fetchall()]: 
    37         if not unique: 
    38             continue 
    39         cursor.execute('PRAGMA index_info(%s)' % quote_name(index)) 
    40         info = cursor.fetchall() 
    41         # Skip indexes across multiple fields 
    42         if len(info) != 1: 
    43             continue 
    44         name = info[0][2] # seqno, cid, name 
    45         indexes[name]['unique'] = True 
    46     return indexes 
    47  
    48 def _table_info(cursor, name): 
    49     cursor.execute('PRAGMA table_info(%s)' % quote_name(name)) 
    50     # cid, name, type, notnull, dflt_value, pk 
    51     return [{'name': field[1], 
    52              'type': field[2], 
    53              'null_ok': not field[3], 
    54              'pk': field[5]     # undocumented 
    55              } for field in cursor.fetchall()] 
    56  
    57 # Maps SQL types to Django Field types. Some of the SQL types have multiple 
    58 # entries here because SQLite allows for anything and doesn't normalize the 
    59 # 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  
    743# This light wrapper "fakes" a dictionary interface, because some SQLite data 
    754# types include variables in them -- e.g. "varchar(30)" -- and can't be matched 
    765# as a simple dictionary lookup. 
    776class 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 
    7824    def __getitem__(self, key): 
    7925        key = key.lower() 
    8026        try: 
    81             return BASE_DATA_TYPES_REVERSE[key] 
     27            return self.base_data_types_reverse[key] 
    8228        except KeyError: 
    8329            import re 
    8430            m = re.search(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$', key) 
     
    8632                return ('CharField', {'max_length': int(m.group(1))}) 
    8733            raise KeyError 
    8834 
    89 DATA_TYPES_REVERSE = FlexibleFieldLookupDict() 
     35class DatabaseIntrospection(BaseDatabaseIntrospection): 
     36    data_types_reverse = FlexibleFieldLookupDict() 
     37 
     38    def __init__(self, ops): 
     39        self.ops = ops 
     40         
     41    def get_table_list(self, cursor): 
     42        "Returns a list of table names in the current database." 
     43        # Skip the sqlite_sequence system table used for autoincrement key 
     44        # generation. 
     45        cursor.execute(""" 
     46            SELECT name FROM sqlite_master 
     47            WHERE type='table' AND NOT name='sqlite_sequence' 
     48            ORDER BY name""") 
     49        return [row[0] for row in cursor.fetchall()] 
     50 
     51    def get_table_description(self, cursor, table_name): 
     52        "Returns a description of the table, with the DB-API cursor.description interface." 
     53        return [(info['name'], info['type'], None, None, None, None, 
     54                 info['null_ok']) for info in self._table_info(cursor, table_name)] 
     55 
     56    def get_relations(self, cursor, table_name): 
     57        raise NotImplementedError 
     58 
     59    def get_indexes(self, cursor, table_name): 
     60        """ 
     61        Returns a dictionary of fieldname -> infodict for the given table, 
     62        where each infodict is in the format: 
     63            {'primary_key': boolean representing whether it's the primary key, 
     64             'unique': boolean representing whether it's a unique index} 
     65        """ 
     66        indexes = {} 
     67        for info in self._table_info(cursor, table_name): 
     68            indexes[info['name']] = {'primary_key': info['pk'] != 0, 
     69                                     'unique': False} 
     70        cursor.execute('PRAGMA index_list(%s)' % self.ops.quote_name(table_name)) 
     71        # seq, name, unique 
     72        for index, unique in [(field[1], field[2]) for field in cursor.fetchall()]: 
     73            if not unique: 
     74                continue 
     75            cursor.execute('PRAGMA index_info(%s)' % self.ops.quote_name(index)) 
     76            info = cursor.fetchall() 
     77            # Skip indexes across multiple fields 
     78            if len(info) != 1: 
     79                continue 
     80            name = info[0][2] # seqno, cid, name 
     81            indexes[name]['unique'] = True 
     82        return indexes 
     83 
     84    def _table_info(self, cursor, name): 
     85        cursor.execute('PRAGMA table_info(%s)' % self.ops.quote_name(name)) 
     86        # cid, name, type, notnull, dflt_value, pk 
     87        return [{'name': field[1], 
     88                 'type': field[2], 
     89                 'null_ok': not field[3], 
     90                 'pk': field[5]     # undocumented 
     91                 } for field in cursor.fetchall()] 
     92 
  • 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 
     1import os 
     2import sys 
     3from django.conf import settings 
     4from django.db.backends.creation import BaseDatabaseCreation 
     5 
     6class 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 _create_test_db(self, connection, verbosity, autoclobber): 
     36        if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:": 
     37            test_database_name = settings.TEST_DATABASE_NAME 
     38            # Erase the old test database 
     39            if verbosity >= 1: 
     40                print "Destroying old test database..." 
     41            if os.access(test_database_name, os.F_OK): 
     42                if not autoclobber: 
     43                    confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % test_database_name) 
     44                if autoclobber or confirm == 'yes': 
     45                  try: 
     46                      if verbosity >= 1: 
     47                          print "Destroying old test database..." 
     48                      os.remove(test_database_name) 
     49                  except Exception, e: 
     50                      sys.stderr.write("Got an error deleting the old test database: %s\n" % e) 
     51                      sys.exit(2) 
     52                else: 
     53                    print "Tests cancelled." 
     54                    sys.exit(1) 
     55            if verbosity >= 1: 
     56                print "Creating test database..." 
     57        else: 
     58            test_database_name = ":memory:" 
     59        return test_database_name 
     60         
     61    def _destroy_test_db(self, connection, test_database_name, verbosity): 
     62        if test_database_name and test_database_name != ":memory:": 
     63            # Remove the SQLite database file 
     64            os.remove(test_database_name)         
     65                     
  • django/db/backends/mysql/base.py

    old new  
    44Requires MySQLdb: http://sourceforge.net/projects/mysql-python 
    55""" 
    66 
    7 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util 
     7from django.db.backends import BaseDatabaseFeatures 
     8from django.db.backends import BaseDatabaseOperations 
     9from django.db.backends import BaseDatabaseWrapper 
     10from django.db.backends import util 
     11from django.db.backends.mysql.client import DatabaseClient 
     12from django.db.backends.mysql.creation import DatabaseCreation 
     13from django.db.backends.mysql.introspection import DatabaseIntrospection 
     14from django.db.backends.mysql.validation import DatabaseValidation 
     15 
    816try: 
    917    import MySQLdb as Database 
    1018except ImportError, e: 
     
    144152class DatabaseWrapper(BaseDatabaseWrapper): 
    145153    features = DatabaseFeatures() 
    146154    ops = DatabaseOperations() 
     155    client = DatabaseClient() 
     156    creation = DatabaseCreation(ops, features) 
     157    introspection = DatabaseIntrospection(ops) 
     158    validation = DatabaseValidation() 
     159     
    147160    operators = { 
    148161        'exact': '= BINARY %s', 
    149162        'iexact': 'LIKE %s', 
  • django/db/backends/mysql/validation.py

    old new  
     1from django.db.backends import BaseDatabaseValidation 
     2 
     3class DatabaseValidation(BaseDatabaseValidation): 
     4    def validate_field(self, errors, opts, f): 
     5        "Prior to MySQL 5.0.3, character fields could not exceed 255 characters" 
     6        from django.db import models 
     7        from django.db import connection 
     8        db_version = connection.get_server_version() 
     9        if db_version < (5, 0, 3) and isinstance(f, (models.CharField, models.CommaSeparatedIntegerField, models.SlugField)) and f.max_length > 255: 
     10            errors.add(opts, 
     11                '"%s": %s cannot have a "max_length" greater than 255 when you are using a version of MySQL prior to 5.0.3 (you are using %s).' %  
     12                (f.name, f.__class__.__name__, '.'.join([str(n) for n in db_version[:3]]))) 
     13     
  • django/db/backends/mysql/client.py

    old new  
     1from django.db.backends import BaseDatabaseClient 
    12from django.conf import settings 
    23import os 
    34 
    4 def runshell(): 
    5     args = [''] 
    6     db = settings.DATABASE_OPTIONS.get('db', settings.DATABASE_NAME) 
    7     user = settings.DATABASE_OPTIONS.get('user', settings.DATABASE_USER) 
    8     passwd = settings.DATABASE_OPT