Ticket #7560: 7560_get_db_prep_refactor.3.diff

File 7560_get_db_prep_refactor.3.diff, 31.1 KB (added by Sean Legassick, 16 years ago)

Fixes for TimeField.to_python and extra tests

  • django/db/models/fields/__init__.py

     
    217217        "Returns field's value just before saving."
    218218        return getattr(model_instance, self.attname)
    219219
     220    def get_db_prep_value(self, value):
     221        """Returns field's value prepared for interacting with the database
     222        backend.
     223
     224        Used by the default implementations of ``get_db_prep_save``and
     225        `get_db_prep_lookup```
     226        """
     227        return value
     228
    220229    def get_db_prep_save(self, value):
    221230        "Returns field's value prepared for saving into a database."
    222         return value
     231        return self.get_db_prep_value(value)
    223232
    224233    def get_db_prep_lookup(self, lookup_type, value):
    225234        "Returns field's value prepared for database lookup."
    226235        if hasattr(value, 'as_sql'):
    227236            sql, params = value.as_sql()
    228237            return QueryWrapper(('(%s)' % sql), params)
    229         if lookup_type in ('exact', 'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'):
     238        if lookup_type in ('regex', 'iregex', 'month', 'day', 'search'):
    230239            return [value]
     240        elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
     241            return [self.get_db_prep_value(value)]
    231242        elif lookup_type in ('range', 'in'):
    232             return value
     243            return [self.get_db_prep_value(v) for v in value]
    233244        elif lookup_type in ('contains', 'icontains'):
    234245            return ["%%%s%%" % connection.ops.prep_for_like_query(value)]
    235246        elif lookup_type == 'iexact':
     
    245256                value = int(value)
    246257            except ValueError:
    247258                raise ValueError("The __year lookup type requires an integer argument")
    248             if settings.DATABASE_ENGINE == 'sqlite3':
    249                 first = '%s-01-01'
    250                 second = '%s-12-31 23:59:59.999999'
    251             elif not connection.features.date_field_supports_time_value and self.get_internal_type() == 'DateField':
    252                 first = '%s-01-01'
    253                 second = '%s-12-31'
    254             elif not connection.features.supports_usecs:
    255                 first = '%s-01-01 00:00:00'
    256                 second = '%s-12-31 23:59:59.99'
     259
     260            if self.get_internal_type() == 'DateField':
     261                return connection.ops.year_lookup_bounds_for_date_field(value)
    257262            else:
    258                 first = '%s-01-01 00:00:00'
    259                 second = '%s-12-31 23:59:59.999999'
    260             return [first % value, second % value]
     263                return connection.ops.year_lookup_bounds(value)
     264
    261265        raise TypeError("Field has invalid lookup: %s" % lookup_type)
    262266
    263267    def has_default(self):
     
    449453        except (TypeError, ValueError):
    450454            raise validators.ValidationError, _("This value must be an integer.")
    451455
     456    def get_db_prep_value(self, value):
     457        if value is None:
     458            return None
     459        return int(value)
     460
    452461    def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
    453462        if not rel:
    454463            return [] # Don't add a FormField unless it's in a related context.
     
    490499        if value in ('f', 'False', '0'): return False
    491500        raise validators.ValidationError, _("This value must be either True or False.")
    492501
     502    def get_db_prep_value(self, value):
     503        if value is None:
     504            return None
     505        return bool(value)
     506
    493507    def get_manipulator_field_objs(self):
    494508        return [oldforms.CheckboxField]
    495509
     
    551565        except ValueError:
    552566            raise validators.ValidationError, _('Enter a valid date in YYYY-MM-DD format.')
    553567
    554     def get_db_prep_lookup(self, lookup_type, value):
    555         if lookup_type in ('range', 'in'):
    556             value = [smart_unicode(v) for v in value]
    557         elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte') and hasattr(value, 'strftime'):
    558             value = datetime_safe.new_date(value).strftime('%Y-%m-%d')
    559         else:
    560             value = smart_unicode(value)
    561         return Field.get_db_prep_lookup(self, lookup_type, value)
    562 
    563568    def pre_save(self, model_instance, add):
    564569        if self.auto_now or (self.auto_now_add and add):
    565570            value = datetime.datetime.now()
     
    583588        else:
    584589            return self.editable or self.auto_now or self.auto_now_add
    585590
    586     def get_db_prep_save(self, value):
    587         # Casts dates into string format for entry into database.
    588         if value is not None:
    589             try:
    590                 value = datetime_safe.new_date(value).strftime('%Y-%m-%d')
    591             except AttributeError:
    592                 # If value is already a string it won't have a strftime method,
    593                 # so we'll just let it pass through.
    594                 pass
    595         return Field.get_db_prep_save(self, value)
     591    def get_db_prep_value(self, value):
     592        # Casts dates into the format expected by the backend
     593        return connection.ops.value_to_db_date(self.to_python(value))
    596594
    597595    def get_manipulator_field_objs(self):
    598596        return [oldforms.DateField]
     
    621619            return value
    622620        if isinstance(value, datetime.date):
    623621            return datetime.datetime(value.year, value.month, value.day)
     622
     623        # Attempt to parse a datetime:
     624        value = smart_str(value)
     625        # split usecs, because they are not recognized by strptime.
     626        if '.' in value:
     627            try:
     628                value, usecs = value.split('.')
     629                usecs = int(usecs)
     630            except ValueError:
     631                raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM[ss[.uuuuuu]] format.')
     632        else:
     633            usecs = 0
     634        kwargs = {'microsecond': usecs}
    624635        try: # Seconds are optional, so try converting seconds first.
    625             return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6])
     636            return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6],
     637                                     **kwargs)
     638
    626639        except ValueError:
    627640            try: # Try without seconds.
    628                 return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M')[:5])
     641                return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M')[:5],
     642                                         **kwargs)
    629643            except ValueError: # Try without hour/minutes/seconds.
    630644                try:
    631                     return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3])
     645                    return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3],
     646                                             **kwargs)
    632647                except ValueError:
    633                     raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM format.')
     648                    raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM[ss[.uuuuuu]] format.')
    634649
    635     def get_db_prep_save(self, value):
    636         # Casts dates into string format for entry into database.
    637         if value is not None:
    638             # MySQL will throw a warning if microseconds are given, because it
    639             # doesn't support microseconds.
    640             if not connection.features.supports_usecs and hasattr(value, 'microsecond'):
    641                 value = value.replace(microsecond=0)
    642             value = smart_unicode(value)
    643         return Field.get_db_prep_save(self, value)
     650    def get_db_prep_value(self, value):
     651        # Casts dates into the format expected by the backend
     652        return connection.ops.value_to_db_datetime(self.to_python(value))
    644653
    645     def get_db_prep_lookup(self, lookup_type, value):
    646         if lookup_type in ('range', 'in'):
    647             value = [smart_unicode(v) for v in value]
    648         else:
    649             value = smart_unicode(value)
    650         return Field.get_db_prep_lookup(self, lookup_type, value)
    651 
    652654    def get_manipulator_field_objs(self):
    653655        return [oldforms.DateField, oldforms.TimeField]
    654656
     
    712714        Formats a number into a string with the requisite number of digits and
    713715        decimal places.
    714716        """
    715         num_chars = self.max_digits
    716         # Allow for a decimal point
    717         if self.decimal_places > 0:
    718             num_chars += 1
    719         # Allow for a minus sign
    720         if value < 0:
    721             num_chars += 1
     717        # Method moved to django.db.backends.util.
     718        #
     719        # It is preserved because it is used by the oracle backend
     720        # (django.db.backends.oracle.query), and also for
     721        # backwards-compatibility with any external code which may have used
     722        # this method.
     723        from django.db.backends import util
     724        return util.format_number(value, self.max_digits, self.decimal_places)
    722725
    723         return u"%.*f" % (self.decimal_places, value)
     726    def get_db_prep_value(self, value):
     727        return connection.ops.value_to_db_decimal(value, self.max_digits,
     728                                                  self.decimal_places)
    724729
    725     def get_db_prep_save(self, value):
    726         value = self._format(value)
    727         return super(DecimalField, self).get_db_prep_save(value)
    728 
    729     def get_db_prep_lookup(self, lookup_type, value):
    730         if lookup_type in ('range', 'in'):
    731             value = [self._format(v) for v in value]
    732         else:
    733             value = self._format(value)
    734         return super(DecimalField, self).get_db_prep_lookup(lookup_type, value)
    735 
    736730    def get_manipulator_field_objs(self):
    737731        return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
    738732
     
    770764    def get_internal_type(self):
    771765        return "FileField"
    772766
    773     def get_db_prep_save(self, value):
     767    def get_db_prep_value(self, value):
    774768        "Returns field's value prepared for saving into a database."
    775769        # Need to convert UploadedFile objects provided via a form to unicode for database insertion
    776770        if hasattr(value, 'name'):
     
    911905class FloatField(Field):
    912906    empty_strings_allowed = False
    913907
     908    def get_db_prep_value(self, value):
     909        if value is None:
     910            return None
     911        return float(value)
     912
    914913    def get_manipulator_field_objs(self):
    915914        return [oldforms.FloatField]
    916915
     
    958957
    959958class IntegerField(Field):
    960959    empty_strings_allowed = False
     960    def get_db_prep_value(self, value):
     961        if value is None:
     962            return None
     963        return int(value)
     964
    961965    def get_manipulator_field_objs(self):
    962966        return [oldforms.IntegerField]
    963967
     
    10051009        if value in ('f', 'False', '0'): return False
    10061010        raise validators.ValidationError, _("This value must be either None, True or False.")
    10071011
     1012    def get_db_prep_value(self, value):
     1013        if value is None:
     1014            return None
     1015        return bool(value)
     1016
    10081017    def get_manipulator_field_objs(self):
    10091018        return [oldforms.NullBooleanField]
    10101019
     
    10171026        defaults.update(kwargs)
    10181027        return super(NullBooleanField, self).formfield(**defaults)
    10191028
    1020 class PhoneNumberField(IntegerField):
     1029class PhoneNumberField(Field):
    10211030    def get_manipulator_field_objs(self):
    10221031        return [oldforms.PhoneNumberField]
    10231032
     
    10991108    def get_internal_type(self):
    11001109        return "TimeField"
    11011110
    1102     def get_db_prep_lookup(self, lookup_type, value):
    1103         if connection.features.time_field_needs_date:
    1104             # Oracle requires a date in order to parse.
    1105             def prep(value):
    1106                 if isinstance(value, datetime.time):
    1107                     value = datetime.datetime.combine(datetime.date(1900, 1, 1), value)
    1108                 return smart_unicode(value)
     1111    def to_python(self, value):
     1112        if value is None:
     1113            return None
     1114        if isinstance(value, datetime.time):
     1115            return value
     1116
     1117        # Attempt to parse a datetime:
     1118        value = smart_str(value)
     1119        # split usecs, because they are not recognized by strptime.
     1120        if '.' in value:
     1121            try:
     1122                value, usecs = value.split('.')
     1123                usecs = int(usecs)
     1124            except ValueError:
     1125                raise validators.ValidationError, _('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.')
    11091126        else:
    1110             prep = smart_unicode
    1111         if lookup_type in ('range', 'in'):
    1112             value = [prep(v) for v in value]
    1113         else:
    1114             value = prep(value)
    1115         return Field.get_db_prep_lookup(self, lookup_type, value)
     1127            usecs = 0
     1128        kwargs = {'microsecond': usecs}
    11161129
     1130        try: # Seconds are optional, so try converting seconds first.
     1131            return datetime.time(*time.strptime(value, '%H:%M:%S')[3:6],
     1132                                 **kwargs)
     1133        except ValueError:
     1134            try: # Try without seconds.
     1135                return datetime.time(*time.strptime(value, '%H:%M')[3:5],
     1136                                         **kwargs)
     1137            except ValueError:
     1138                raise validators.ValidationError, _('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.')
     1139
    11171140    def pre_save(self, model_instance, add):
    11181141        if self.auto_now or (self.auto_now_add and add):
    11191142            value = datetime.datetime.now().time()
     
    11221145        else:
    11231146            return super(TimeField, self).pre_save(model_instance, add)
    11241147
    1125     def get_db_prep_save(self, value):
    1126         # Casts dates into string format for entry into database.
    1127         if value is not None:
    1128             # MySQL will throw a warning if microseconds are given, because it
    1129             # doesn't support microseconds.
    1130             if not connection.features.supports_usecs and hasattr(value, 'microsecond'):
    1131                 value = value.replace(microsecond=0)
    1132             if connection.features.time_field_needs_date:
    1133                 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field.
    1134                 if isinstance(value, datetime.time):
    1135                     value = datetime.datetime(1900, 1, 1, value.hour, value.minute,
    1136                                               value.second, value.microsecond)
    1137                 elif isinstance(value, basestring):
    1138                     value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6]))
    1139             else:
    1140                 value = smart_unicode(value)
    1141         return Field.get_db_prep_save(self, value)
     1148    def get_db_prep_value(self, value):
     1149        # Casts times into the format expected by the backend
     1150        return connection.ops.value_to_db_time(self.to_python(value))
    11421151
    11431152    def get_manipulator_field_objs(self):
    11441153        return [oldforms.TimeField]
  • django/db/backends/sqlite3/base.py

     
    8484        # sql_flush() implementations). Just return SQL at this point
    8585        return sql
    8686
     87    def year_lookup_bounds(self, value):
     88        first = '%s-01-01'
     89        second = '%s-12-31 23:59:59.999999'
     90        return [first % value, second % value]
     91
     92
    8793class DatabaseWrapper(BaseDatabaseWrapper):
    8894    features = DatabaseFeatures()
    8995    ops = DatabaseOperations()
     
    159165        dt = util.typecast_timestamp(dt)
    160166    except (ValueError, TypeError):
    161167        return None
    162     return str(getattr(dt, lookup_type))
     168    return getattr(dt, lookup_type)
    163169
    164170def _sqlite_date_trunc(lookup_type, dt):
    165171    try:
  • django/db/backends/util.py

     
    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/backends/mysql/base.py

     
    6363    inline_fk_references = False
    6464    empty_fetchmany_value = ()
    6565    update_can_self_select = False
    66     supports_usecs = False
    6766
    6867class DatabaseOperations(BaseDatabaseOperations):
    6968    def date_extract_sql(self, lookup_type, field_name):
     
    124123        else:
    125124            return []
    126125
     126    def value_to_db_datetime(self, value):
     127        # MySQL doesn't support microseconds
     128        if value is None:
     129            return None
     130        # XXX: Why not just str(value)?
     131        return unicode(value.replace(microsecond=0))
     132
     133    def value_to_db_time(self, value):
     134        # MySQL doesn't support microseconds
     135        if value is None:
     136            return None
     137        # XXX: Why not just str(value)?
     138        return unicode(value.replace(microsecond=0))
     139
     140    def year_lookup_bounds(self, value):
     141        # Again, no microseconds
     142        first = '%s-01-01 00:00:00'
     143        second = '%s-12-31 23:59:59.99'
     144        return [first % value, second % value]
     145
    127146class DatabaseWrapper(BaseDatabaseWrapper):
    128147    features = DatabaseFeatures()
    129148    ops = DatabaseOperations()
  • django/db/backends/oracle/base.py

     
    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
     
    2830    supports_tablespaces = True
    2931    uses_case_insensitive_names = True
    3032    uses_custom_query_class = True
    31     time_field_needs_date = True
    3233    interprets_empty_strings_as_nulls = True
    33     date_field_supports_time_value = False
    3434
    3535class DatabaseOperations(BaseDatabaseOperations):
    3636    def autoinc_sql(self, table, column):
     
    180180    def tablespace_sql(self, tablespace, inline=False):
    181181        return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace))
    182182
     183    def value_to_db_time(self, value):
     184        if value is None:
     185            return None
     186        if isinstance(value, basestring):
     187            return datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6]))
     188        return datetime.datetime(1900, 1, 1, value.hour, value.minute,
     189                                 value.second, value.microsecond)
     190
     191    def year_lookup_bounds_for_date_field(self, value):
     192        first = '%s-01-01'
     193        second = '%s-12-31'
     194        return [first % value, second % value]
     195
     196
     197
    183198class DatabaseWrapper(BaseDatabaseWrapper):
    184199    features = DatabaseFeatures()
    185200    ops = DatabaseOperations()
  • django/db/backends/__init__.py

     
    55    # Import copy of _thread_local.py from Python 2.4
    66    from django.utils._threading_local import local
    77
     8from django.db.backends import util
     9from django.utils import datetime_safe
     10
    811class BaseDatabaseWrapper(local):
    912    """
    1013    Represents a database connection.
     
    3639        return cursor
    3740
    3841    def make_debug_cursor(self, cursor):
    39         from django.db.backends import util
    4042        return util.CursorDebugWrapper(cursor, self)
    4143
    4244class BaseDatabaseFeatures(object):
    4345    allows_group_by_ordinal = True
    4446    inline_fk_references = True
    45     needs_datetime_string_cast = True
     47    needs_datetime_string_cast = True # uses
     48                                      # django.db.backend.utils.typecast_timetamp
     49                                      # on returned values from dates()?
    4650    supports_constraints = True
    4751    supports_tablespaces = False
    4852    uses_case_insensitive_names = False
    4953    uses_custom_query_class = False
    5054    empty_fetchmany_value = []
    5155    update_can_self_select = True
    52     supports_usecs = True
    53     time_field_needs_date = False
    5456    interprets_empty_strings_as_nulls = False
    55     date_field_supports_time_value = True
    5657    can_use_chunked_reads = True
    5758
    5859class BaseDatabaseOperations(object):
     
    263264        """Prepares a value for use in a LIKE query."""
    264265        from django.utils.encoding import smart_unicode
    265266        return smart_unicode(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_")
     267
     268    def value_to_db_date(self, value):
     269        """
     270        Transform a date value to an object compatible with what is expected
     271        by the backend driver for date columns.
     272        """
     273        if value is None:
     274            return None
     275        return datetime_safe.new_date(value).strftime('%Y-%m-%d')
     276
     277    def value_to_db_datetime(self, value):
     278        """
     279        Transform a datetime value to an object compatible with what is expected
     280        by the backend driver for date columns.
     281        """
     282        if value is None:
     283            return None
     284        return unicode(value) # XXX: Why not just str(value)?
     285
     286    def value_to_db_time(self, value):
     287        """
     288        Transform a datetime value to an object compatible with what is expected
     289        by the backend driver for date columns.
     290        """
     291        if value is None:
     292            return None
     293        return unicode(value) # XXX: Why not just str(value)?
     294
     295    def value_to_db_decimal(self, value, max_digits, decimal_places):
     296        """
     297        Transform a decimal.Decimal value to an object compatible with what is
     298        expected by the backend driver for decimal (numeric) columns.
     299        """
     300        if value is None:
     301            return None
     302        return util.format_number(value, max_digits, decimal_places)
     303
     304    def year_lookup_bounds(self, value):
     305        """
     306        Returns a two-elements list with the lower and upper bound to be used
     307        with a BETWEEN operator to query a field value using a year lookup
     308
     309        `value` is an int, containing the looked-up year.
     310        """
     311        first = '%s-01-01 00:00:00'
     312        second = '%s-12-31 23:59:59.999999'
     313        return [first % value, second % value]
     314
     315    def year_lookup_bounds_for_date_field(self, value):
     316        """
     317        Returns a two-elements list with the lower and upper bound to be used
     318        with a BETWEEN operator to query a DateField value using a year lookup
     319
     320        `value` is an int, containing the looked-up year.
     321
     322        By default, it just calls `self.year_lookup_bounds`. Some backends need
     323        this hook because on their DB date fields can't be compared to values
     324        which include a time part.
     325        """
     326        return self.year_lookup_bounds(value)
     327
  • tests/modeltests/validation/models.py

     
    1616    birthdate = models.DateField()
    1717    favorite_moment = models.DateTimeField()
    1818    email = models.EmailField()
     19    best_time = models.TimeField()
    1920
    2021    def __unicode__(self):
    2122        return self.name
     
    2829...     'name': 'John',
    2930...     'birthdate': datetime.date(2000, 5, 3),
    3031...     'favorite_moment': datetime.datetime(2002, 4, 3, 13, 23),
    31 ...     'email': 'john@example.com'
     32...     'email': 'john@example.com',
     33...     'best_time': datetime.time(16, 20),
    3234... }
    3335>>> p = Person(**valid_params)
    3436>>> p.validate()
     
    130132>>> p.favorite_moment
    131133datetime.datetime(2002, 4, 3, 0, 0)
    132134
     135>>> p = Person(**dict(valid_params, best_time='16:20:00'))
     136>>> p.validate()
     137{}
     138>>> p.best_time
     139datetime.time(16, 20)
     140
     141>>> p = Person(**dict(valid_params, best_time='16:20'))
     142>>> p.validate()
     143{}
     144>>> p.best_time
     145datetime.time(16, 20)
     146
     147>>> p = Person(**dict(valid_params, best_time='bar'))
     148>>> p.validate()['best_time']
     149[u'Enter a valid time in HH:MM[:ss[.uuuuuu]] format.']
     150
    133151>>> p = Person(**dict(valid_params, email='john@example.com'))
    134152>>> p.validate()
    135153{}
     
    153171[u'This field is required.']
    154172>>> errors['birthdate']
    155173[u'This field is required.']
     174>>> errors['best_time']
     175[u'This field is required.']
    156176
    157177"""}
  • tests/modeltests/custom_methods/models.py

     
    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()]
  • tests/regressiontests/model_regress/models.py

     
    2929class Party(models.Model):
    3030    when = models.DateField()
    3131
     32class Event(models.Model):
     33    when = models.DateTimeField()
     34
    3235__test__ = {'API_TESTS': """
    3336(NOTE: Part of the regression test here is merely parsing the model
    3437declaration. The verbose_name, in particular, did not always work.)
     
    6871>>> [p.when for p in Party.objects.filter(when__year = 1998)]
    6972[datetime.date(1998, 12, 31)]
    7073
     74# Check that get_next_by_FIELD and get_previous_by_FIELD don't crash when we
     75# have usecs values stored on the database
     76#
     77# [It crashed after the Field.get_db_prep_* refactor, because on most backends
     78#  DateTimeFields supports usecs, but DateTimeField.to_python didn't recognize
     79#  them. (Note that Model._get_next_or_previous_by_FIELD coerces values to
     80#  strings)]
     81#
     82>>> e = Event.objects.create(when = datetime.datetime(2000, 1, 1, 16, 0, 0))
     83>>> e = Event.objects.create(when = datetime.datetime(2000, 1, 1, 6, 1, 1))
     84>>> e = Event.objects.create(when = datetime.datetime(2000, 1, 1, 13, 1, 1))
     85>>> e = Event.objects.create(when = datetime.datetime(2000, 1, 1, 12, 0, 20, 24))
     86>>> e.get_next_by_when().when
     87datetime.datetime(2000, 1, 1, 13, 1, 1)
     88>>> e.get_previous_by_when().when
     89datetime.datetime(2000, 1, 1, 6, 1, 1)
    7190"""
    7291}
  • tests/regressiontests/model_fields/tests.py

     
    2020>>> x = f.to_python(2)
    2121>>> y = f.to_python('2.6')
    2222
    23 >>> f.get_db_prep_save(x)
     23>>> f._format(x)
    2424u'2.0'
    25 >>> f.get_db_prep_save(y)
     25>>> f._format(y)
    2626u'2.6'
    27 >>> f.get_db_prep_save(None)
    28 >>> f.get_db_prep_lookup('exact', x)
    29 [u'2.0']
    30 >>> f.get_db_prep_lookup('exact', y)
    31 [u'2.6']
     27>>> f._format(None)
    3228>>> f.get_db_prep_lookup('exact', None)
    3329[None]
    3430
     31# DateTimeField and TimeField to_python should support usecs:
     32>>> f = DateTimeField()
     33>>> f.to_python('2001-01-02 03:04:05.000006')
     34datetime.datetime(2001, 1, 2, 3, 4, 5, 6)
     35>>> f.to_python('2001-01-02 03:04:05.999999')
     36datetime.datetime(2001, 1, 2, 3, 4, 5, 999999)
     37
     38>>> f = TimeField()
     39>>> f.to_python('01:02:03.000004')
     40datetime.time(1, 2, 3, 4)
     41>>> f.to_python('01:02:03.999999')
     42datetime.time(1, 2, 3, 999999)
     43
     44
    3545"""
  • docs/custom_model_fields.txt

     
    385385called when it is created, you should be using `The SubfieldBase metaclass`_
    386386mentioned earlier. Otherwise ``to_python()`` won't be called automatically.
    387387
    388 ``get_db_prep_save(self, value)``
    389 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     388``get_db_prep_value(self, value)``
     389~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    390390
    391391This is the reverse of ``to_python()`` when working with the database backends
    392392(as opposed to serialization). The ``value`` parameter is the current value of
     
    399399    class HandField(models.Field):
    400400        # ...
    401401
    402         def get_db_prep_save(self, value):
     402        def get_db_prep_value(self, value):
    403403            return ''.join([''.join(l) for l in (value.north,
    404404                    value.east, value.south, value.west)])
    405405
     406``get_db_prep_save(self, value)``
     407~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     408
     409Same as the above, but called when the Field value must be *saved* to the
     410database. As the default implementation just calls ``get_db_prep_value``, you
     411shouldn't need to implement this method unless your custom field need a special
     412conversion when being saved that is not the same as the used for normal query
     413parameters (which is implemented by ``get_db_prep_value``).
     414
     415
    406416``pre_save(self, model_instance, add)``
    407417~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    408418
     
    440450and pass the rest of the ``get_db_prep_lookup()`` method of the parent class.
    441451
    442452If you needed to implement ``get_db_prep_save()``, you will usually need to
    443 implement ``get_db_prep_lookup()``. The usual reason is because of the
    444 ``range``  and ``in`` lookups. In these case, you will passed a list of
    445 objects (presumably of the right type) and will need to convert them to a list
    446 of things of the right type for passing to the database. Sometimes you can
    447 reuse ``get_db_prep_save()``, or at least factor out some common pieces from
    448 both methods into a help function.
     453implement ``get_db_prep_lookup()``. If you don't, ``get_db_prep_value`` will be
     454called by the default implementation, to manage ``exact``, ``gt``, ``gte``,
     455``lt``, ``lte``, ``in`` and ``range`` lookups.
    449456
    450 For example::
     457You may also want to implement this method to limit the lookup types that could
     458be used with your custom field type.
    451459
     460Note that, for ``range`` and ``in`` lookups, ``get_db_prep_lookup`` will receive
     461a list of objects (presumably of the right type) and will need to convert them
     462to a list of things of the right type for passing to the database. Most of the
     463time, you can reuse ``get_db_prep_value()``, or at least factor out some common
     464pieces.
     465
     466For example, the following code implements ``get_db_prep_lookup`` to limit the
     467accepted lookup types to ``exact`` and ``in``::
     468
    452469    class HandField(models.Field):
    453470        # ...
    454471
    455472        def get_db_prep_lookup(self, lookup_type, value):
    456473            # We only handle 'exact' and 'in'. All others are errors.
    457474            if lookup_type == 'exact':
    458                 return self.get_db_prep_save(value)
     475                return self.get_db_prep_value(value)
    459476            elif lookup_type == 'in':
    460                 return [self.get_db_prep_save(v) for v in value]
     477                return [self.get_db_prep_value(v) for v in value]
    461478            else:
    462479                raise TypeError('Lookup type %r not supported.' % lookup_type)
    463480
     
    557574
    558575        def flatten_data(self, follow, obj=None):
    559576            value = self._get_val_from_obj(obj)
    560             return {self.attname: self.get_db_prep_save(value)}
     577            return {self.attname: self.get_db_prep_value(value)}
    561578
    562579Some general advice
    563580--------------------
Back to Top