Ticket #7560: get_db_prep_refactor.patch

File get_db_prep_refactor.patch, 18.7 KB (added by Leo Soto M., 16 years ago)
  • django/db/backends/__init__.py

    diff -r ae5842633149 django/db/backends/__init__.py
    a b except ImportError:  
    44except ImportError:
    55    # Import copy of _thread_local.py from Python 2.4
    66    from django.utils._threading_local import local
     7
     8from django.db.backends import util
    79
    810class BaseDatabaseWrapper(local):
    911    """
    class BaseDatabaseWrapper(local):  
    3638        return cursor
    3739
    3840    def make_debug_cursor(self, cursor):
    39         from django.db.backends import util
    4041        return util.CursorDebugWrapper(cursor, self)
    4142
    4243class BaseDatabaseFeatures(object):
    class BaseDatabaseFeatures(object):  
    4445    allows_unique_and_pk = True
    4546    autoindexes_primary_keys = True
    4647    inline_fk_references = True
    47     needs_datetime_string_cast = True
     48    needs_datetime_string_cast = True # uses
     49                                      # django.db.backend.utils.typecast_timetamp
     50                                      # on returned values from dates()
    4851    supports_constraints = True
    4952    supports_tablespaces = False
    5053    uses_case_insensitive_names = False
    class BaseDatabaseOperations(object):  
    274277        """Prepares a value for use in a LIKE query."""
    275278        from django.utils.encoding import smart_unicode
    276279        return smart_unicode(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_")
     280
     281    def value_to_db_date(self, value):
     282        """
     283        Transform a date value to an object compatible with what is expected
     284        by the backend driver for date columns.
     285        """
     286        if value is None:
     287            return None
     288        return value.strftime('%Y-%m-%d')
     289
     290    def value_to_db_datetime(self, value):
     291        """
     292        Transform a datetime value to an object compatible with what is expected
     293        by the backend driver for date columns.
     294        """
     295        if value is None:
     296            return None
     297        return unicode(value) # XXX: Why not just str(value)?
     298
     299    def value_to_db_time(self, value):
     300        """
     301        Transform a datetime value to an object compatible with what is expected
     302        by the backend driver for date columns.
     303        """
     304        if value is None:
     305            return None
     306        return unicode(value) # XXX: Why not just str(value)?
     307
     308    def value_to_db_decimal(self, value, max_digits, decimal_places):
     309        """
     310        Transform a decimal.Decimal value to an object compatible with what is
     311        expected by the backend driver for decimal (numeric) columns.
     312        """
     313        if value is None:
     314            return None
     315        return util.format_number(value, max_digits, decimal_places)
  • django/db/backends/mysql/base.py

    diff -r ae5842633149 django/db/backends/mysql/base.py
    a b class DatabaseOperations(BaseDatabaseOpe  
    132132        else:
    133133            return []
    134134
     135    def value_to_db_datetime(self, value):
     136        # MySQL doesn't support microseconds
     137        if value is None:
     138            return None
     139        # XXX: Why not just str(value)?
     140        return unicode(value.replace(microsecond=0))
     141
     142    def value_to_db_time(self, value):
     143        # MySQL doesn't support microseconds
     144        if value is None:
     145            return None
     146        # XXX: Why not just str(value)?
     147        return unicode(value.replace(microsecond=0))
     148
    135149class DatabaseWrapper(BaseDatabaseWrapper):
    136150    features = DatabaseFeatures()
    137151    ops = DatabaseOperations()
  • django/db/backends/oracle/base.py

    diff -r ae5842633149 django/db/backends/oracle/base.py
    a b Requires cx_Oracle: http://www.python.ne  
    55"""
    66
    77import os
     8import datetime
     9import time
    810
    911from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
    1012from django.db.backends.oracle import query
    class DatabaseOperations(BaseDatabaseOpe  
    183185
    184186    def tablespace_sql(self, tablespace, inline=False):
    185187        return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace))
     188
     189    def value_to_db_time(self, value):
     190        if value is None:
     191            return None
     192        if isinstance(value, basestring):
     193            return datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6]))
     194        return datetime.datetime(1900, 1, 1, value.hour, value.minute,
     195                                 value.second, value.microsecond)
    186196
    187197class DatabaseWrapper(BaseDatabaseWrapper):
    188198    features = DatabaseFeatures()
  • django/db/backends/sqlite3/base.py

    diff -r ae5842633149 django/db/backends/sqlite3/base.py
    a b def _sqlite_extract(lookup_type, dt):  
    150150    try:
    151151        dt = util.typecast_timestamp(dt)
    152152    except (ValueError, TypeError):
     153        print "error!"
    153154        return None
    154     return str(getattr(dt, lookup_type))
     155    return getattr(dt, lookup_type)
    155156
    156157def _sqlite_date_trunc(lookup_type, dt):
    157158    try:
  • django/db/backends/util.py

    diff -r ae5842633149 django/db/backends/util.py
    a b def truncate_name(name, length=None):  
    117117    hash = md5.md5(name).hexdigest()[:4]
    118118
    119119    return '%s%s' % (name[:length-4], hash)
     120
     121def format_number(value, max_digits, decimal_places):
     122    """
     123    Formats a number into a string with the requisite number of digits and
     124    decimal places.
     125    """
     126    return u"%.*f" % (decimal_places, value)
  • django/db/models/fields/__init__.py

    diff -r ae5842633149 django/db/models/fields/__init__.py
    a b class Field(object):  
    248248                value = int(value)
    249249            except ValueError:
    250250                raise ValueError("The __year lookup type requires an integer argument")
     251            if settings.DATABASE_ENGINE.endswith('zxjdbc'):
     252                # zxJDBC drivers should support dates
     253                first = datetime.datetime(value, 1, 1)
     254                second = datetime.datetime(value, 12, 31,
     255                                           23, 59, 59, 999999)
     256                return (first, second)
    251257            if settings.DATABASE_ENGINE == 'sqlite3':
    252258                first = '%s-01-01'
    253259                second = '%s-12-31 23:59:59.999999'
    class AutoField(Field):  
    449455        except (TypeError, ValueError):
    450456            raise validators.ValidationError, _("This value must be an integer.")
    451457
     458    def get_db_prep_save(self, value):
     459        if value is None:
     460            return None
     461        return int(value)
     462
     463    def get_db_prep_lookup(self, lookup_type, value):
     464        if lookup_type == 'range':
     465            value = [int(v) for v in value]
     466        elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
     467            value = int(value)
     468        return Field.get_db_prep_lookup(self, lookup_type, value)
     469
    452470    def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
    453471        if not rel:
    454472            return [] # Don't add a FormField unless it's in a related context.
    class BooleanField(Field):  
    487505        if value in ('t', 'True', '1'): return True
    488506        if value in ('f', 'False', '0'): return False
    489507        raise validators.ValidationError, _("This value must be either True or False.")
     508
     509    def get_db_prep_save(self, value):
     510        if value is None:
     511            return None
     512        return bool(value)
     513
     514    def get_db_prep_lookup(self, lookup_type, value):
     515        if lookup_type == 'exact' and value is not None:
     516            value = bool(value)
     517        return Field.get_db_prep_lookup(self, lookup_type, value)
    490518
    491519    def get_manipulator_field_objs(self):
    492520        return [oldforms.CheckboxField]
    class DateField(Field):  
    550578            raise validators.ValidationError, _('Enter a valid date in YYYY-MM-DD format.')
    551579
    552580    def get_db_prep_lookup(self, lookup_type, value):
     581        value_to_db_date = connection.ops.value_to_db_date
    553582        if lookup_type == 'range':
    554             value = [smart_unicode(v) for v in value]
    555         elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte') and hasattr(value, 'strftime'):
    556             value = value.strftime('%Y-%m-%d')
    557         else:
    558             value = smart_unicode(value)
     583            value = [value_to_db_date(self.to_python(v)) for v in value]
     584        elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
     585            # Lookups with a date argument
     586            value = value_to_db_date(self.to_python(value))
     587        # else:
     588        #    value = smart_unicode(value)
    559589        return Field.get_db_prep_lookup(self, lookup_type, value)
    560590
    561591    def pre_save(self, model_instance, add):
    class DateField(Field):  
    582612            return self.editable or self.auto_now or self.auto_now_add
    583613
    584614    def get_db_prep_save(self, value):
    585         # Casts dates into string format for entry into database.
    586         if value is not None:
    587             try:
    588                 value = value.strftime('%Y-%m-%d')
    589             except AttributeError:
    590                 # If value is already a string it won't have a strftime method,
    591                 # so we'll just let it pass through.
    592                 pass
    593         return Field.get_db_prep_save(self, value)
     615        # Casts dates into the format expected by the backend
     616        return connection.ops.value_to_db_date(self.to_python(value))
    594617
    595618    def get_manipulator_field_objs(self):
    596619        return [oldforms.DateField]
    class DateTimeField(DateField):  
    627650                    raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM format.')
    628651
    629652    def get_db_prep_save(self, value):
    630         # Casts dates into string format for entry into database.
    631         if value is not None:
    632             # MySQL will throw a warning if microseconds are given, because it
    633             # doesn't support microseconds.
    634             if not connection.features.supports_usecs and hasattr(value, 'microsecond'):
    635                 value = value.replace(microsecond=0)
    636             value = smart_unicode(value)
    637         return Field.get_db_prep_save(self, value)
     653        # Casts dates into the format expected by the backend
     654        return connection.ops.value_to_db_datetime(self.to_python(value))
    638655
    639656    def get_db_prep_lookup(self, lookup_type, value):
     657        value_to_db_datetime = connection.ops.value_to_db_datetime
    640658        if lookup_type == 'range':
    641             value = [smart_unicode(v) for v in value]
    642         else:
    643             value = smart_unicode(value)
     659            value = [value_to_db_datetime(self.to_python(v)) for v in value]
     660        elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
     661            value = value_to_db_datetime(self.to_python(value))
    644662        return Field.get_db_prep_lookup(self, lookup_type, value)
    645663
    646664    def get_manipulator_field_objs(self):
    class DecimalField(Field):  
    701719        Formats a number into a string with the requisite number of digits and
    702720        decimal places.
    703721        """
    704         num_chars = self.max_digits
    705         # Allow for a decimal point
    706         if self.decimal_places > 0:
    707             num_chars += 1
    708         # Allow for a minus sign
    709         if value < 0:
    710             num_chars += 1
    711 
    712         return u"%.*f" % (self.decimal_places, value)
     722        # Method moved to django.db.backends.util.
     723        #
     724        # It is preserved because it is used by the oracle backend
     725        # (django.db.backends.oracle.query), and also for
     726        # backwards-compatibility with any external code which may have used
     727        # this method.
     728        from django.db.backends import util
     729        return util.format_number(value, self.max_digits, self.decimal_places)
    713730
    714731    def get_db_prep_save(self, value):
    715         if value is not None:
    716             value = self._format(value)
    717         return super(DecimalField, self).get_db_prep_save(value)
     732        return connection.ops.value_to_db_decimal(value, self.max_digits,
     733                                                  self.decimal_places)
    718734
    719735    def get_db_prep_lookup(self, lookup_type, value):
     736        value_to_db_decimal = connection.ops.value_to_db_decimal
    720737        if lookup_type == 'range':
    721             value = [self._format(v) for v in value]
     738            value = [value_to_db_decimal(v, self.max_digits,
     739                                         self.decimal_places)
     740                     for v in value]
    722741        else:
    723             value = self._format(value)
     742            value = value_to_db_decimal(value, self.max_digits,
     743                                        self.decimal_places)
    724744        return super(DecimalField, self).get_db_prep_lookup(lookup_type, value)
    725745
    726746    def get_manipulator_field_objs(self):
    class FloatField(Field):  
    885905class FloatField(Field):
    886906    empty_strings_allowed = False
    887907
     908    def get_db_prep_save(self, value):
     909        if value is None:
     910            return None
     911        return float(value)
     912
     913    def get_db_prep_lookup(self, lookup_type, value):
     914        if lookup_type == 'range':
     915            value = [float(v) for v in value]
     916        elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
     917            value = float(value)
     918        return Field.get_db_prep_lookup(self, lookup_type, value)
     919
    888920    def get_manipulator_field_objs(self):
    889921        return [oldforms.FloatField]
    890922
    class ImageField(FileField):  
    935967
    936968class IntegerField(Field):
    937969    empty_strings_allowed = False
     970    def get_db_prep_save(self, value):
     971        if value is None:
     972            return None
     973        return int(value)
     974
     975    def get_db_prep_lookup(self, lookup_type, value):
     976        if lookup_type == 'range':
     977            value = [int(v) for v in value]
     978        elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
     979            value = int(value)
     980        return Field.get_db_prep_lookup(self, lookup_type, value)
     981
    938982    def get_manipulator_field_objs(self):
    939983        return [oldforms.IntegerField]
    940984
    class NullBooleanField(Field):  
    9821026        if value in ('f', 'False', '0'): return False
    9831027        raise validators.ValidationError, _("This value must be either None, True or False.")
    9841028
     1029    def get_db_prep_save(self, value):
     1030        if value is None:
     1031            return None
     1032        return bool(value)
     1033
     1034    def get_db_prep_lookup(self, lookup_type, value):
     1035        if lookup_type == 'exact' and value is not None:
     1036            value = bool(value)
     1037        return Field.get_db_prep_lookup(self, lookup_type, value)
     1038
     1039
    9851040    def get_manipulator_field_objs(self):
    9861041        return [oldforms.NullBooleanField]
    9871042
    class NullBooleanField(Field):  
    9901045        defaults.update(kwargs)
    9911046        return super(NullBooleanField, self).formfield(**defaults)
    9921047
    993 class PhoneNumberField(IntegerField):
     1048class PhoneNumberField(Field):
    9941049    def get_manipulator_field_objs(self):
    9951050        return [oldforms.PhoneNumberField]
    9961051
    class TimeField(Field):  
    10721127    def get_internal_type(self):
    10731128        return "TimeField"
    10741129
     1130    def to_python(self, value):
     1131        if value is None:
     1132            return None
     1133        if isinstance(value, datetime.time):
     1134            return value
     1135        try: # Seconds are optional, so try converting seconds first.
     1136            return datetime.time(*time.strptime(value, '%H:%M:%S')[3:6])
     1137        except ValueError:
     1138            try: # Try without seconds.
     1139                return datetime.datetime(*time.strptime(value, '%H:%M')[:5])
     1140            except ValueError:
     1141                raise validators.ValidationError, _('Enter a valid time in HH:MM[:ss] format.')
     1142
    10751143    def get_db_prep_lookup(self, lookup_type, value):
    1076         if connection.features.time_field_needs_date:
    1077             # Oracle requires a date in order to parse.
    1078             def prep(value):
    1079                 if isinstance(value, datetime.time):
    1080                     value = datetime.datetime.combine(datetime.date(1900, 1, 1), value)
    1081                 return smart_unicode(value)
    1082         else:
    1083             prep = smart_unicode
     1144        value_to_db_time = connection.ops.value_to_db_time
    10841145        if lookup_type == 'range':
    1085             value = [prep(v) for v in value]
    1086         else:
    1087             value = prep(value)
     1146            value = [value_to_db_time(self.to_python(v)) for v in value]
     1147        elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
     1148            value = value_to_db_time(self.to_python(value))
    10881149        return Field.get_db_prep_lookup(self, lookup_type, value)
    10891150
    10901151    def pre_save(self, model_instance, add):
    class TimeField(Field):  
    10961157            return super(TimeField, self).pre_save(model_instance, add)
    10971158
    10981159    def get_db_prep_save(self, value):
    1099         # Casts dates into string format for entry into database.
    1100         if value is not None:
    1101             # MySQL will throw a warning if microseconds are given, because it
    1102             # doesn't support microseconds.
    1103             if not connection.features.supports_usecs and hasattr(value, 'microsecond'):
    1104                 value = value.replace(microsecond=0)
    1105             if connection.features.time_field_needs_date:
    1106                 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field.
    1107                 if isinstance(value, datetime.time):
    1108                     value = datetime.datetime(1900, 1, 1, value.hour, value.minute,
    1109                                               value.second, value.microsecond)
    1110                 elif isinstance(value, basestring):
    1111                     value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6]))
    1112             else:
    1113                 value = smart_unicode(value)
    1114         return Field.get_db_prep_save(self, value)
     1160        # Casts times into the format expected by the backend
     1161        return connection.ops.value_to_db_time(self.to_python(value))
    11151162
    11161163    def get_manipulator_field_objs(self):
    11171164        return [oldforms.TimeField]
  • tests/modeltests/custom_methods/models.py

    diff -r ae5842633149 tests/modeltests/custom_methods/models.py
    a b class Article(models.Model):  
    3131            SELECT id, headline, pub_date
    3232            FROM custom_methods_article
    3333            WHERE pub_date = %s
    34                 AND id != %s""", [str(self.pub_date), self.id])
     34                AND id != %s""", [connection.ops.value_to_db_date(self.pub_date),
     35                                  self.id])
    3536        # The asterisk in "(*row)" tells Python to expand the list into
    3637        # positional arguments to Article().
    3738        return [self.__class__(*row) for row in cursor.fetchall()]
Back to Top