app/django/db/backends/__init__.py
changeset 323 ff1a9aa48cfd
parent 54 03e267d67478
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
     2     # Only exists in Python 2.4+
     2     # Only exists in Python 2.4+
     3     from threading import local
     3     from threading import local
     4 except ImportError:
     4 except ImportError:
     5     # Import copy of _thread_local.py from Python 2.4
     5     # Import copy of _thread_local.py from Python 2.4
     6     from django.utils._threading_local import local
     6     from django.utils._threading_local import local
       
     7 try:
       
     8     set
       
     9 except NameError:
       
    10     # Python 2.3 compat
       
    11     from sets import Set as set
       
    12 
       
    13 from django.db.backends import util
       
    14 from django.utils import datetime_safe
     7 
    15 
     8 class BaseDatabaseWrapper(local):
    16 class BaseDatabaseWrapper(local):
     9     """
    17     """
    10     Represents a database connection.
    18     Represents a database connection.
    11     """
    19     """
    20             return self.connection.commit()
    28             return self.connection.commit()
    21 
    29 
    22     def _rollback(self):
    30     def _rollback(self):
    23         if self.connection is not None:
    31         if self.connection is not None:
    24             return self.connection.rollback()
    32             return self.connection.rollback()
       
    33 
       
    34     def _savepoint(self, sid):
       
    35         if not self.features.uses_savepoints:
       
    36             return
       
    37         self.connection.cursor().execute(self.ops.savepoint_create_sql(sid))
       
    38 
       
    39     def _savepoint_rollback(self, sid):
       
    40         if not self.features.uses_savepoints:
       
    41             return
       
    42         self.connection.cursor().execute(self.ops.savepoint_rollback_sql(sid))
       
    43 
       
    44     def _savepoint_commit(self, sid):
       
    45         if not self.features.uses_savepoints:
       
    46             return
       
    47         self.connection.cursor().execute(self.ops.savepoint_commit_sql(sid))
    25 
    48 
    26     def close(self):
    49     def close(self):
    27         if self.connection is not None:
    50         if self.connection is not None:
    28             self.connection.close()
    51             self.connection.close()
    29             self.connection = None
    52             self.connection = None
    34         if settings.DEBUG:
    57         if settings.DEBUG:
    35             return self.make_debug_cursor(cursor)
    58             return self.make_debug_cursor(cursor)
    36         return cursor
    59         return cursor
    37 
    60 
    38     def make_debug_cursor(self, cursor):
    61     def make_debug_cursor(self, cursor):
    39         from django.db.backends import util
       
    40         return util.CursorDebugWrapper(cursor, self)
    62         return util.CursorDebugWrapper(cursor, self)
    41 
    63 
    42 class BaseDatabaseFeatures(object):
    64 class BaseDatabaseFeatures(object):
    43     allows_group_by_ordinal = True
    65     # True if django.db.backend.utils.typecast_timestamp is used on values
    44     allows_unique_and_pk = True
    66     # returned from dates() calls.
    45     autoindexes_primary_keys = True
       
    46     inline_fk_references = True
       
    47     needs_datetime_string_cast = True
    67     needs_datetime_string_cast = True
    48     needs_upper_for_iops = False
       
    49     supports_constraints = True
       
    50     supports_tablespaces = False
       
    51     uses_case_insensitive_names = False
       
    52     uses_custom_query_class = False
    68     uses_custom_query_class = False
    53     empty_fetchmany_value = []
    69     empty_fetchmany_value = []
    54     update_can_self_select = True
    70     update_can_self_select = True
       
    71     interprets_empty_strings_as_nulls = False
       
    72     can_use_chunked_reads = True
       
    73     uses_savepoints = False
       
    74     # If True, don't use integer foreign keys referring to, e.g., positive
       
    75     # integer primary keys.
       
    76     related_fields_match_type = False
    55 
    77 
    56 class BaseDatabaseOperations(object):
    78 class BaseDatabaseOperations(object):
    57     """
    79     """
    58     This class encapsulates all backend-specific differences, such as the way
    80     This class encapsulates all backend-specific differences, such as the way
    59     a backend performs ordering or calculates the ID of a recently-inserted
    81     a backend performs ordering or calculates the ID of a recently-inserted
   158         This method also receives the table name and the name of the primary-key
   180         This method also receives the table name and the name of the primary-key
   159         column.
   181         column.
   160         """
   182         """
   161         return cursor.lastrowid
   183         return cursor.lastrowid
   162 
   184 
   163     def limit_offset_sql(self, limit, offset=None):
       
   164         """
       
   165         Returns a LIMIT/OFFSET SQL clause, given a limit and optional offset.
       
   166         """
       
   167         # 'LIMIT 40 OFFSET 20'
       
   168         sql = "LIMIT %s" % limit
       
   169         if offset and offset != 0:
       
   170             sql += " OFFSET %s" % offset
       
   171         return sql
       
   172 
       
   173     def lookup_cast(self, lookup_type):
   185     def lookup_cast(self, lookup_type):
   174         """
   186         """
   175         Returns the string to use in a query when performing lookups
   187         Returns the string to use in a query when performing lookups
   176         ("contains", "like", etc). The resulting string should contain a '%s'
   188         ("contains", "like", etc). The resulting string should contain a '%s'
   177         placeholder for the column being searched against.
   189         placeholder for the column being searched against.
   200         """
   212         """
   201         return 'DEFAULT'
   213         return 'DEFAULT'
   202 
   214 
   203     def query_class(self, DefaultQueryClass):
   215     def query_class(self, DefaultQueryClass):
   204         """
   216         """
   205         Given the default QuerySet class, returns a custom QuerySet class
   217         Given the default Query class, returns a custom Query class
   206         to use for this backend. Returns None if a custom QuerySet isn't used.
   218         to use for this backend. Returns None if a custom Query isn't used.
   207         See also BaseDatabaseFeatures.uses_custom_query_class, which regulates
   219         See also BaseDatabaseFeatures.uses_custom_query_class, which regulates
   208         whether this method is called at all.
   220         whether this method is called at all.
   209         """
   221         """
   210         return None
   222         return None
   211 
   223 
   231         If the feature is not supported (or part of it is not supported), a
   243         If the feature is not supported (or part of it is not supported), a
   232         NotImplementedError exception can be raised.
   244         NotImplementedError exception can be raised.
   233         """
   245         """
   234         raise NotImplementedError
   246         raise NotImplementedError
   235 
   247 
       
   248     def savepoint_create_sql(self, sid):
       
   249         """
       
   250         Returns the SQL for starting a new savepoint. Only required if the
       
   251         "uses_savepoints" feature is True. The "sid" parameter is a string
       
   252         for the savepoint id.
       
   253         """
       
   254         raise NotImplementedError
       
   255 
       
   256     def savepoint_commit_sql(self, sid):
       
   257         """
       
   258         Returns the SQL for committing the given savepoint.
       
   259         """
       
   260         raise NotImplementedError
       
   261 
       
   262     def savepoint_rollback_sql(self, sid):
       
   263         """
       
   264         Returns the SQL for rolling back the given savepoint.
       
   265         """
       
   266         raise NotImplementedError
       
   267 
   236     def sql_flush(self, style, tables, sequences):
   268     def sql_flush(self, style, tables, sequences):
   237         """
   269         """
   238         Returns a list of SQL statements required to remove all data from
   270         Returns a list of SQL statements required to remove all data from
   239         the given database tables (without actually removing the tables
   271         the given database tables (without actually removing the tables
   240         themselves).
   272         themselves).
   260         """
   292         """
   261         return "BEGIN;"
   293         return "BEGIN;"
   262 
   294 
   263     def tablespace_sql(self, tablespace, inline=False):
   295     def tablespace_sql(self, tablespace, inline=False):
   264         """
   296         """
   265         Returns the tablespace SQL, or None if the backend doesn't use
   297         Returns the SQL that will be appended to tables or rows to define
   266         tablespaces.
   298         a tablespace. Returns '' if the backend doesn't use tablespaces.
   267         """
   299         """
   268         return None
   300         return ''
       
   301 
       
   302     def prep_for_like_query(self, x):
       
   303         """Prepares a value for use in a LIKE query."""
       
   304         from django.utils.encoding import smart_unicode
       
   305         return smart_unicode(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_")
       
   306 
       
   307     # Same as prep_for_like_query(), but called for "iexact" matches, which
       
   308     # need not necessarily be implemented using "LIKE" in the backend.
       
   309     prep_for_iexact_query = prep_for_like_query
       
   310 
       
   311     def value_to_db_date(self, value):
       
   312         """
       
   313         Transform a date value to an object compatible with what is expected
       
   314         by the backend driver for date columns.
       
   315         """
       
   316         if value is None:
       
   317             return None
       
   318         return datetime_safe.new_date(value).strftime('%Y-%m-%d')
       
   319 
       
   320     def value_to_db_datetime(self, value):
       
   321         """
       
   322         Transform a datetime value to an object compatible with what is expected
       
   323         by the backend driver for datetime columns.
       
   324         """
       
   325         if value is None:
       
   326             return None
       
   327         return unicode(value)
       
   328 
       
   329     def value_to_db_time(self, value):
       
   330         """
       
   331         Transform a datetime value to an object compatible with what is expected
       
   332         by the backend driver for time columns.
       
   333         """
       
   334         if value is None:
       
   335             return None
       
   336         return unicode(value)
       
   337 
       
   338     def value_to_db_decimal(self, value, max_digits, decimal_places):
       
   339         """
       
   340         Transform a decimal.Decimal value to an object compatible with what is
       
   341         expected by the backend driver for decimal (numeric) columns.
       
   342         """
       
   343         if value is None:
       
   344             return None
       
   345         return util.format_number(value, max_digits, decimal_places)
       
   346 
       
   347     def year_lookup_bounds(self, value):
       
   348         """
       
   349         Returns a two-elements list with the lower and upper bound to be used
       
   350         with a BETWEEN operator to query a field value using a year lookup
       
   351 
       
   352         `value` is an int, containing the looked-up year.
       
   353         """
       
   354         first = '%s-01-01 00:00:00'
       
   355         second = '%s-12-31 23:59:59.999999'
       
   356         return [first % value, second % value]
       
   357 
       
   358     def year_lookup_bounds_for_date_field(self, value):
       
   359         """
       
   360         Returns a two-elements list with the lower and upper bound to be used
       
   361         with a BETWEEN operator to query a DateField value using a year lookup
       
   362 
       
   363         `value` is an int, containing the looked-up year.
       
   364 
       
   365         By default, it just calls `self.year_lookup_bounds`. Some backends need
       
   366         this hook because on their DB date fields can't be compared to values
       
   367         which include a time part.
       
   368         """
       
   369         return self.year_lookup_bounds(value)
       
   370 
       
   371 class BaseDatabaseIntrospection(object):
       
   372     """
       
   373     This class encapsulates all backend-specific introspection utilities
       
   374     """
       
   375     data_types_reverse = {}
       
   376 
       
   377     def __init__(self, connection):
       
   378         self.connection = connection
       
   379 
       
   380     def table_name_converter(self, name):
       
   381         """Apply a conversion to the name for the purposes of comparison.
       
   382 
       
   383         The default table name converter is for case sensitive comparison.
       
   384         """
       
   385         return name
       
   386 
       
   387     def table_names(self):
       
   388         "Returns a list of names of all tables that exist in the database."
       
   389         cursor = self.connection.cursor()
       
   390         return self.get_table_list(cursor)
       
   391 
       
   392     def django_table_names(self, only_existing=False):
       
   393         """
       
   394         Returns a list of all table names that have associated Django models and
       
   395         are in INSTALLED_APPS.
       
   396 
       
   397         If only_existing is True, the resulting list will only include the tables
       
   398         that actually exist in the database.
       
   399         """
       
   400         from django.db import models
       
   401         tables = set()
       
   402         for app in models.get_apps():
       
   403             for model in models.get_models(app):
       
   404                 tables.add(model._meta.db_table)
       
   405                 tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many])
       
   406         if only_existing:
       
   407             tables = [t for t in tables if t in self.table_names()]
       
   408         return tables
       
   409 
       
   410     def installed_models(self, tables):
       
   411         "Returns a set of all models represented by the provided list of table names."
       
   412         from django.db import models
       
   413         all_models = []
       
   414         for app in models.get_apps():
       
   415             for model in models.get_models(app):
       
   416                 all_models.append(model)
       
   417         return set([m for m in all_models
       
   418             if self.table_name_converter(m._meta.db_table) in map(self.table_name_converter, tables)
       
   419         ])
       
   420 
       
   421     def sequence_list(self):
       
   422         "Returns a list of information about all DB sequences for all models in all apps."
       
   423         from django.db import models
       
   424 
       
   425         apps = models.get_apps()
       
   426         sequence_list = []
       
   427 
       
   428         for app in apps:
       
   429             for model in models.get_models(app):
       
   430                 for f in model._meta.local_fields:
       
   431                     if isinstance(f, models.AutoField):
       
   432                         sequence_list.append({'table': model._meta.db_table, 'column': f.column})
       
   433                         break # Only one AutoField is allowed per model, so don't bother continuing.
       
   434 
       
   435                 for f in model._meta.local_many_to_many:
       
   436                     sequence_list.append({'table': f.m2m_db_table(), 'column': None})
       
   437 
       
   438         return sequence_list
       
   439 
       
   440 class BaseDatabaseClient(object):
       
   441     """
       
   442     This class encapsulates all backend-specific methods for opening a
       
   443     client shell.
       
   444     """
       
   445     # This should be a string representing the name of the executable
       
   446     # (e.g., "psql"). Subclasses must override this.
       
   447     executable_name = None
       
   448 
       
   449     def runshell(self):
       
   450         raise NotImplementedError()
       
   451 
       
   452 class BaseDatabaseValidation(object):
       
   453     """
       
   454     This class encapsualtes all backend-specific model validation.
       
   455     """
       
   456     def validate_field(self, errors, opts, f):
       
   457         "By default, there is no backend-specific validation"
       
   458         pass
       
   459