Django

Code

Ticket #5461: 5461-r8225.2.diff

File 5461-r8225.2.diff, 167.1 kB (added by russellm, 5 months ago)

rc2 for Creation backend patch - mysql/validation.py was missing from rc1

  • 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.test import signals 
    76from django.template import Template 
    87from django.utils.translation import deactivate 
    98 
    10 # The prefix to put on the default database name when creating 
    11 # the test database. 
    12 TEST_DATABASE_PREFIX = 'test_' 
    13  
    149def instrumented_test_render(self, context): 
    1510    """ 
    1611    An instrumented Template render method, providing a signal 
     
    7065 
    7166    del mail.outbox 
    7267 
    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 = True 
    80     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_CHARSET 
    95     return '' 
    96  
    97 def create_test_db(verbosity=1, autoclobber=False): 
    98     """ 
    99     Creates a test database, prompting the user for confirmation if the 
    100     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 it 
    103     creation_module = get_creation_module() 
    104     if hasattr(creation_module, "create_test_db"): 
    105         creation_module.create_test_db(settings, connection, verbosity, autoclobber) 
    106         return 
    107  
    108     if verbosity >= 1: 
    109         print "Creating test database..." 
    110     # If we're using SQLite, it's more convenient to test against an 
    111     # in-memory database. Using the TEST_DATABASE_NAME setting you can still choose 
    112     # 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_NAME 
    116             # Erase the old test database 
    117             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_NAME 
    145         else: 
    146             TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME 
    147  
    148         qn = connection.ops.quote_name 
    149  
    150         # Create the test database and connect to it. We need to autocommit 
    151         # if the database supports it because PostgreSQL doesn't allow 
    152         # 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_NAME 
    178  
    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 has 
    186     # the side effect of initializing the test database. 
    187     cursor = connection.cursor() 
    188  
    189     return TEST_DATABASE_NAME 
    190  
    191 def destroy_test_db(old_database_name, verbosity=1): 
    192     # If the database wants to drop the test DB itself, let it 
    193     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         return 
    197  
    198     if verbosity >= 1: 
    199         print "Destroying test database..." 
    200     connection.close() 
    201     TEST_DATABASE_NAME = settings.DATABASE_NAME 
    202     settings.DATABASE_NAME = old_database_name 
    203     if settings.DATABASE_ENGINE == "sqlite3": 
    204         if TEST_DATABASE_NAME and TEST_DATABASE_NAME != ":memory:": 
    205             # Remove the SQLite database file 
    206             os.remove(TEST_DATABASE_NAME) 
    207     else: 
    208         # Remove the test database to clean up after 
    209         # ourselves. Connect to the previous database (not the test database) 
    210         # to do so, because it's not allowed to delete a database while being 
    211         # 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  
    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  
    1414    # backends that ships with Django, so look there first. 
    1515    _import_path = 'django.db.backends.' 
    1616    backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, ['']) 
    17     creation = __import__('%s%s.creation' % (_import_path, settings.DATABASE_ENGINE), {}, {}, ['']) 
    1817except ImportError, e: 
    1918    # If the import failed, we might be looking for a database backend 
    2019    # distributed external to Django. So we'll try that next. 
    2120    try: 
    2221        _import_path = '' 
    2322        backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, ['']) 
    24         creation = __import__('%s.creation' % settings.DATABASE_ENGINE, {}, {}, ['']) 
    2523    except ImportError, e_user: 
    2624        # The database backend wasn't found. Display a helpful error message 
    2725        # listing all possible (built-in) database backends. 
     
    2927        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')] 
    3028        available_backends.sort() 
    3129        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))) 
    3432        else: 
    3533            raise # If there's some other error, this must be an error in Django itself. 
    3634 
    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, so 
    42 # lazily load it on demmand. 
    43 get_introspection_module = curry(_import_database_module, _import_path, 'introspection') 
    44  
    45 def get_creation_module(): 
    46     return creation 
    47  
    48 # We want runshell() to work the same way, but we have to treat it a 
    49 # little differently (since it just runs instead of returning a module like 
    50 # the above) and wrap the lazily-loaded runshell() method. 
    51 runshell = lambda: _import_database_module(_import_path, "client").runshell() 
    52  
    5335# Convenient aliases for backend bits. 
    5436connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS) 
    5537DatabaseError = 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): 
    66     features = DatabaseFeatures() 
    67     ops = DatabaseOperations() 
    6876    operators = { 
    6977        'exact': '= %s', 
    7078        'iexact': 'ILIKE %s', 
     
    8290        'iendswith': 'ILIKE %s', 
    8391    } 
    8492 
     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 
    85103    def _cursor(self, settings): 
    86104        set_tz = False 
    87105        if self.connection is None: 
  • 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 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()] 
    431 
    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 
    1536 
    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 
    2057 
    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 
    4186 
    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 
     
    3947Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal) 
    4048 
    4149class DatabaseFeatures(BaseDatabaseFeatures): 
    42     supports_constraints = False 
    4350    # SQLite cannot handle us only partially reading from a cursor's result set 
    4451    # and then writing the same rows to the database in another cursor. This 
    4552    # setting ensures we always read result sets fully into memory all in one 
     
    8996        second = '%s-12-31 23:59:59.999999' 
    9097        return [first % value, second % value] 
    9198 
     99class DatabaseValidation(BaseDatabaseValidation): 
     100    pass 
    92101 
    93102class DatabaseWrapper(BaseDatabaseWrapper): 
    94     features = DatabaseFeatures() 
    95     ops = DatabaseOperations() 
    96  
     103     
    97104    # SQLite requires LIKE statements to include an ESCAPE clause if the value 
    98105    # being escaped has a percent or underscore in it. 
    99106    # See http://www.sqlite.org/lang_expr.html for an explanation. 
     
    114121        'iendswith': "LIKE %s ESCAPE '\\'", 
    115122    } 
    116123 
     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 
    117134    def _cursor(self, settings): 
    118135        if self.connection is None: 
    119136            if not settings.DATABASE_NAME: 
  • 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 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 
     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 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  
    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 i