Commit b3b71a09 authored by Malcolm Tredinnick's avatar Malcolm Tredinnick
Browse files

Fixed #7560 -- Moved a lot of the value conversion preparation for

loading/saving interactions with the databases into django.db.backend. This
helps external db backend writers and removes a bunch of database-specific
if-tests in django.db.models.fields.

Great work from Leo Soto.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@8131 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 7bc728c8
Loading
Loading
Loading
Loading
+66 −4
Original line number Diff line number Diff line
@@ -5,6 +5,9 @@ except ImportError:
    # Import copy of _thread_local.py from Python 2.4
    from django.utils._threading_local import local

from django.db.backends import util
from django.utils import datetime_safe

class BaseDatabaseWrapper(local):
    """
    Represents a database connection.
@@ -36,12 +39,13 @@ class BaseDatabaseWrapper(local):
        return cursor

    def make_debug_cursor(self, cursor):
        from django.db.backends import util
        return util.CursorDebugWrapper(cursor, self)

class BaseDatabaseFeatures(object):
    allows_group_by_ordinal = True
    inline_fk_references = True
    # True if django.db.backend.utils.typecast_timestamp is used on values
    # returned from dates() calls.
    needs_datetime_string_cast = True
    supports_constraints = True
    supports_tablespaces = False
@@ -49,10 +53,7 @@ class BaseDatabaseFeatures(object):
    uses_custom_query_class = False
    empty_fetchmany_value = []
    update_can_self_select = True
    supports_usecs = True
    time_field_needs_date = False
    interprets_empty_strings_as_nulls = False
    date_field_supports_time_value = True
    can_use_chunked_reads = True

class BaseDatabaseOperations(object):
@@ -263,3 +264,64 @@ class BaseDatabaseOperations(object):
        """Prepares a value for use in a LIKE query."""
        from django.utils.encoding import smart_unicode
        return smart_unicode(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_")

    def value_to_db_date(self, value):
        """
        Transform a date value to an object compatible with what is expected
        by the backend driver for date columns.
        """
        if value is None:
            return None
        return datetime_safe.new_date(value).strftime('%Y-%m-%d')

    def value_to_db_datetime(self, value):
        """
        Transform a datetime value to an object compatible with what is expected
        by the backend driver for date columns.
        """
        if value is None:
            return None
        return unicode(value)

    def value_to_db_time(self, value):
        """
        Transform a datetime value to an object compatible with what is expected
        by the backend driver for date columns.
        """
        if value is None:
            return None
        return unicode(value)

    def value_to_db_decimal(self, value, max_digits, decimal_places):
        """
        Transform a decimal.Decimal value to an object compatible with what is
        expected by the backend driver for decimal (numeric) columns.
        """
        if value is None:
            return None
        return util.format_number(value, max_digits, decimal_places)

    def year_lookup_bounds(self, value):
        """
        Returns a two-elements list with the lower and upper bound to be used
        with a BETWEEN operator to query a field value using a year lookup

        `value` is an int, containing the looked-up year.
        """
        first = '%s-01-01 00:00:00'
        second = '%s-12-31 23:59:59.999999'
        return [first % value, second % value]

    def year_lookup_bounds_for_date_field(self, value):
        """
        Returns a two-elements list with the lower and upper bound to be used
        with a BETWEEN operator to query a DateField value using a year lookup

        `value` is an int, containing the looked-up year.

        By default, it just calls `self.year_lookup_bounds`. Some backends need
        this hook because on their DB date fields can't be compared to values
        which include a time part.
        """
        return self.year_lookup_bounds(value)
+18 −1
Original line number Diff line number Diff line
@@ -63,7 +63,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
    inline_fk_references = False
    empty_fetchmany_value = ()
    update_can_self_select = False
    supports_usecs = False

class DatabaseOperations(BaseDatabaseOperations):
    def date_extract_sql(self, lookup_type, field_name):
@@ -124,6 +123,24 @@ class DatabaseOperations(BaseDatabaseOperations):
        else:
            return []

    def value_to_db_datetime(self, value):
        # MySQL doesn't support microseconds
        if value is None:
            return None
        return unicode(value.replace(microsecond=0))

    def value_to_db_time(self, value):
        # MySQL doesn't support microseconds
        if value is None:
            return None
        return unicode(value.replace(microsecond=0))

    def year_lookup_bounds(self, value):
        # Again, no microseconds
        first = '%s-01-01 00:00:00'
        second = '%s-12-31 23:59:59.99'
        return [first % value, second % value]

class DatabaseWrapper(BaseDatabaseWrapper):
    features = DatabaseFeatures()
    ops = DatabaseOperations()
+17 −2
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@ Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
"""

import os
import datetime
import time

from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
from django.db.backends.oracle import query
@@ -28,9 +30,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
    supports_tablespaces = True
    uses_case_insensitive_names = True
    uses_custom_query_class = True
    time_field_needs_date = True
    interprets_empty_strings_as_nulls = True
    date_field_supports_time_value = False

class DatabaseOperations(BaseDatabaseOperations):
    def autoinc_sql(self, table, column):
@@ -180,6 +180,21 @@ class DatabaseOperations(BaseDatabaseOperations):
    def tablespace_sql(self, tablespace, inline=False):
        return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace))

    def value_to_db_time(self, value):
        if value is None:
            return None
        if isinstance(value, basestring):
            return datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6]))
        return datetime.datetime(1900, 1, 1, value.hour, value.minute,
                                 value.second, value.microsecond)

    def year_lookup_bounds_for_date_field(self, value):
        first = '%s-01-01'
        second = '%s-12-31'
        return [first % value, second % value]



class DatabaseWrapper(BaseDatabaseWrapper):
    features = DatabaseFeatures()
    ops = DatabaseOperations()
+7 −1
Original line number Diff line number Diff line
@@ -84,6 +84,12 @@ class DatabaseOperations(BaseDatabaseOperations):
        # sql_flush() implementations). Just return SQL at this point
        return sql

    def year_lookup_bounds(self, value):
        first = '%s-01-01'
        second = '%s-12-31 23:59:59.999999'
        return [first % value, second % value]


class DatabaseWrapper(BaseDatabaseWrapper):
    features = DatabaseFeatures()
    ops = DatabaseOperations()
@@ -159,7 +165,7 @@ def _sqlite_extract(lookup_type, dt):
        dt = util.typecast_timestamp(dt)
    except (ValueError, TypeError):
        return None
    return str(getattr(dt, lookup_type))
    return getattr(dt, lookup_type)

def _sqlite_date_trunc(lookup_type, dt):
    try:
+7 −0
Original line number Diff line number Diff line
@@ -117,3 +117,10 @@ def truncate_name(name, length=None):
    hash = md5.md5(name).hexdigest()[:4]

    return '%s%s' % (name[:length-4], hash)

def format_number(value, max_digits, decimal_places):
    """
    Formats a number into a string with the requisite number of digits and
    decimal places.
    """
    return u"%.*f" % (decimal_places, value)
Loading