Ticket #7560: 7560_get_db_prep_refactor.3.diff
File 7560_get_db_prep_refactor.3.diff, 31.1 KB (added by , 16 years ago) |
---|
-
django/db/models/fields/__init__.py
217 217 "Returns field's value just before saving." 218 218 return getattr(model_instance, self.attname) 219 219 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 220 229 def get_db_prep_save(self, value): 221 230 "Returns field's value prepared for saving into a database." 222 return value231 return self.get_db_prep_value(value) 223 232 224 233 def get_db_prep_lookup(self, lookup_type, value): 225 234 "Returns field's value prepared for database lookup." 226 235 if hasattr(value, 'as_sql'): 227 236 sql, params = value.as_sql() 228 237 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'): 230 239 return [value] 240 elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'): 241 return [self.get_db_prep_value(value)] 231 242 elif lookup_type in ('range', 'in'): 232 return value243 return [self.get_db_prep_value(v) for v in value] 233 244 elif lookup_type in ('contains', 'icontains'): 234 245 return ["%%%s%%" % connection.ops.prep_for_like_query(value)] 235 246 elif lookup_type == 'iexact': … … 245 256 value = int(value) 246 257 except ValueError: 247 258 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) 257 262 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 261 265 raise TypeError("Field has invalid lookup: %s" % lookup_type) 262 266 263 267 def has_default(self): … … 449 453 except (TypeError, ValueError): 450 454 raise validators.ValidationError, _("This value must be an integer.") 451 455 456 def get_db_prep_value(self, value): 457 if value is None: 458 return None 459 return int(value) 460 452 461 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): 453 462 if not rel: 454 463 return [] # Don't add a FormField unless it's in a related context. … … 490 499 if value in ('f', 'False', '0'): return False 491 500 raise validators.ValidationError, _("This value must be either True or False.") 492 501 502 def get_db_prep_value(self, value): 503 if value is None: 504 return None 505 return bool(value) 506 493 507 def get_manipulator_field_objs(self): 494 508 return [oldforms.CheckboxField] 495 509 … … 551 565 except ValueError: 552 566 raise validators.ValidationError, _('Enter a valid date in YYYY-MM-DD format.') 553 567 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 563 568 def pre_save(self, model_instance, add): 564 569 if self.auto_now or (self.auto_now_add and add): 565 570 value = datetime.datetime.now() … … 583 588 else: 584 589 return self.editable or self.auto_now or self.auto_now_add 585 590 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)) 596 594 597 595 def get_manipulator_field_objs(self): 598 596 return [oldforms.DateField] … … 621 619 return value 622 620 if isinstance(value, datetime.date): 623 621 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} 624 635 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 626 639 except ValueError: 627 640 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) 629 643 except ValueError: # Try without hour/minutes/seconds. 630 644 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) 632 647 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.') 634 649 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)) 644 653 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 652 654 def get_manipulator_field_objs(self): 653 655 return [oldforms.DateField, oldforms.TimeField] 654 656 … … 712 714 Formats a number into a string with the requisite number of digits and 713 715 decimal places. 714 716 """ 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) 722 725 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) 724 729 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 736 730 def get_manipulator_field_objs(self): 737 731 return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)] 738 732 … … 770 764 def get_internal_type(self): 771 765 return "FileField" 772 766 773 def get_db_prep_ save(self, value):767 def get_db_prep_value(self, value): 774 768 "Returns field's value prepared for saving into a database." 775 769 # Need to convert UploadedFile objects provided via a form to unicode for database insertion 776 770 if hasattr(value, 'name'): … … 911 905 class FloatField(Field): 912 906 empty_strings_allowed = False 913 907 908 def get_db_prep_value(self, value): 909 if value is None: 910 return None 911 return float(value) 912 914 913 def get_manipulator_field_objs(self): 915 914 return [oldforms.FloatField] 916 915 … … 958 957 959 958 class IntegerField(Field): 960 959 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 961 965 def get_manipulator_field_objs(self): 962 966 return [oldforms.IntegerField] 963 967 … … 1005 1009 if value in ('f', 'False', '0'): return False 1006 1010 raise validators.ValidationError, _("This value must be either None, True or False.") 1007 1011 1012 def get_db_prep_value(self, value): 1013 if value is None: 1014 return None 1015 return bool(value) 1016 1008 1017 def get_manipulator_field_objs(self): 1009 1018 return [oldforms.NullBooleanField] 1010 1019 … … 1017 1026 defaults.update(kwargs) 1018 1027 return super(NullBooleanField, self).formfield(**defaults) 1019 1028 1020 class PhoneNumberField( IntegerField):1029 class PhoneNumberField(Field): 1021 1030 def get_manipulator_field_objs(self): 1022 1031 return [oldforms.PhoneNumberField] 1023 1032 … … 1099 1108 def get_internal_type(self): 1100 1109 return "TimeField" 1101 1110 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.') 1109 1126 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} 1116 1129 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 1117 1140 def pre_save(self, model_instance, add): 1118 1141 if self.auto_now or (self.auto_now_add and add): 1119 1142 value = datetime.datetime.now().time() … … 1122 1145 else: 1123 1146 return super(TimeField, self).pre_save(model_instance, add) 1124 1147 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)) 1142 1151 1143 1152 def get_manipulator_field_objs(self): 1144 1153 return [oldforms.TimeField] -
django/db/backends/sqlite3/base.py
84 84 # sql_flush() implementations). Just return SQL at this point 85 85 return sql 86 86 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 87 93 class DatabaseWrapper(BaseDatabaseWrapper): 88 94 features = DatabaseFeatures() 89 95 ops = DatabaseOperations() … … 159 165 dt = util.typecast_timestamp(dt) 160 166 except (ValueError, TypeError): 161 167 return None 162 return str(getattr(dt, lookup_type))168 return getattr(dt, lookup_type) 163 169 164 170 def _sqlite_date_trunc(lookup_type, dt): 165 171 try: -
django/db/backends/util.py
117 117 hash = md5.md5(name).hexdigest()[:4] 118 118 119 119 return '%s%s' % (name[:length-4], hash) 120 121 def 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
63 63 inline_fk_references = False 64 64 empty_fetchmany_value = () 65 65 update_can_self_select = False 66 supports_usecs = False67 66 68 67 class DatabaseOperations(BaseDatabaseOperations): 69 68 def date_extract_sql(self, lookup_type, field_name): … … 124 123 else: 125 124 return [] 126 125 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 127 146 class DatabaseWrapper(BaseDatabaseWrapper): 128 147 features = DatabaseFeatures() 129 148 ops = DatabaseOperations() -
django/db/backends/oracle/base.py
5 5 """ 6 6 7 7 import os 8 import datetime 9 import time 8 10 9 11 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util 10 12 from django.db.backends.oracle import query … … 28 30 supports_tablespaces = True 29 31 uses_case_insensitive_names = True 30 32 uses_custom_query_class = True 31 time_field_needs_date = True32 33 interprets_empty_strings_as_nulls = True 33 date_field_supports_time_value = False34 34 35 35 class DatabaseOperations(BaseDatabaseOperations): 36 36 def autoinc_sql(self, table, column): … … 180 180 def tablespace_sql(self, tablespace, inline=False): 181 181 return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace)) 182 182 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 183 198 class DatabaseWrapper(BaseDatabaseWrapper): 184 199 features = DatabaseFeatures() 185 200 ops = DatabaseOperations() -
django/db/backends/__init__.py
5 5 # Import copy of _thread_local.py from Python 2.4 6 6 from django.utils._threading_local import local 7 7 8 from django.db.backends import util 9 from django.utils import datetime_safe 10 8 11 class BaseDatabaseWrapper(local): 9 12 """ 10 13 Represents a database connection. … … 36 39 return cursor 37 40 38 41 def make_debug_cursor(self, cursor): 39 from django.db.backends import util40 42 return util.CursorDebugWrapper(cursor, self) 41 43 42 44 class BaseDatabaseFeatures(object): 43 45 allows_group_by_ordinal = True 44 46 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()? 46 50 supports_constraints = True 47 51 supports_tablespaces = False 48 52 uses_case_insensitive_names = False 49 53 uses_custom_query_class = False 50 54 empty_fetchmany_value = [] 51 55 update_can_self_select = True 52 supports_usecs = True53 time_field_needs_date = False54 56 interprets_empty_strings_as_nulls = False 55 date_field_supports_time_value = True56 57 can_use_chunked_reads = True 57 58 58 59 class BaseDatabaseOperations(object): … … 263 264 """Prepares a value for use in a LIKE query.""" 264 265 from django.utils.encoding import smart_unicode 265 266 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
16 16 birthdate = models.DateField() 17 17 favorite_moment = models.DateTimeField() 18 18 email = models.EmailField() 19 best_time = models.TimeField() 19 20 20 21 def __unicode__(self): 21 22 return self.name … … 28 29 ... 'name': 'John', 29 30 ... 'birthdate': datetime.date(2000, 5, 3), 30 31 ... '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), 32 34 ... } 33 35 >>> p = Person(**valid_params) 34 36 >>> p.validate() … … 130 132 >>> p.favorite_moment 131 133 datetime.datetime(2002, 4, 3, 0, 0) 132 134 135 >>> p = Person(**dict(valid_params, best_time='16:20:00')) 136 >>> p.validate() 137 {} 138 >>> p.best_time 139 datetime.time(16, 20) 140 141 >>> p = Person(**dict(valid_params, best_time='16:20')) 142 >>> p.validate() 143 {} 144 >>> p.best_time 145 datetime.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 133 151 >>> p = Person(**dict(valid_params, email='john@example.com')) 134 152 >>> p.validate() 135 153 {} … … 153 171 [u'This field is required.'] 154 172 >>> errors['birthdate'] 155 173 [u'This field is required.'] 174 >>> errors['best_time'] 175 [u'This field is required.'] 156 176 157 177 """} -
tests/modeltests/custom_methods/models.py
31 31 SELECT id, headline, pub_date 32 32 FROM custom_methods_article 33 33 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]) 35 36 # The asterisk in "(*row)" tells Python to expand the list into 36 37 # positional arguments to Article(). 37 38 return [self.__class__(*row) for row in cursor.fetchall()] -
tests/regressiontests/model_regress/models.py
29 29 class Party(models.Model): 30 30 when = models.DateField() 31 31 32 class Event(models.Model): 33 when = models.DateTimeField() 34 32 35 __test__ = {'API_TESTS': """ 33 36 (NOTE: Part of the regression test here is merely parsing the model 34 37 declaration. The verbose_name, in particular, did not always work.) … … 68 71 >>> [p.when for p in Party.objects.filter(when__year = 1998)] 69 72 [datetime.date(1998, 12, 31)] 70 73 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 87 datetime.datetime(2000, 1, 1, 13, 1, 1) 88 >>> e.get_previous_by_when().when 89 datetime.datetime(2000, 1, 1, 6, 1, 1) 71 90 """ 72 91 } -
tests/regressiontests/model_fields/tests.py
20 20 >>> x = f.to_python(2) 21 21 >>> y = f.to_python('2.6') 22 22 23 >>> f. get_db_prep_save(x)23 >>> f._format(x) 24 24 u'2.0' 25 >>> f. get_db_prep_save(y)25 >>> f._format(y) 26 26 u'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) 32 28 >>> f.get_db_prep_lookup('exact', None) 33 29 [None] 34 30 31 # DateTimeField and TimeField to_python should support usecs: 32 >>> f = DateTimeField() 33 >>> f.to_python('2001-01-02 03:04:05.000006') 34 datetime.datetime(2001, 1, 2, 3, 4, 5, 6) 35 >>> f.to_python('2001-01-02 03:04:05.999999') 36 datetime.datetime(2001, 1, 2, 3, 4, 5, 999999) 37 38 >>> f = TimeField() 39 >>> f.to_python('01:02:03.000004') 40 datetime.time(1, 2, 3, 4) 41 >>> f.to_python('01:02:03.999999') 42 datetime.time(1, 2, 3, 999999) 43 44 35 45 """ -
docs/custom_model_fields.txt
385 385 called when it is created, you should be using `The SubfieldBase metaclass`_ 386 386 mentioned earlier. Otherwise ``to_python()`` won't be called automatically. 387 387 388 ``get_db_prep_ save(self, value)``389 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 388 ``get_db_prep_value(self, value)`` 389 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 390 390 391 391 This is the reverse of ``to_python()`` when working with the database backends 392 392 (as opposed to serialization). The ``value`` parameter is the current value of … … 399 399 class HandField(models.Field): 400 400 # ... 401 401 402 def get_db_prep_ save(self, value):402 def get_db_prep_value(self, value): 403 403 return ''.join([''.join(l) for l in (value.north, 404 404 value.east, value.south, value.west)]) 405 405 406 ``get_db_prep_save(self, value)`` 407 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 408 409 Same as the above, but called when the Field value must be *saved* to the 410 database. As the default implementation just calls ``get_db_prep_value``, you 411 shouldn't need to implement this method unless your custom field need a special 412 conversion when being saved that is not the same as the used for normal query 413 parameters (which is implemented by ``get_db_prep_value``). 414 415 406 416 ``pre_save(self, model_instance, add)`` 407 417 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 408 418 … … 440 450 and pass the rest of the ``get_db_prep_lookup()`` method of the parent class. 441 451 442 452 If 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. 453 implement ``get_db_prep_lookup()``. If you don't, ``get_db_prep_value`` will be 454 called by the default implementation, to manage ``exact``, ``gt``, ``gte``, 455 ``lt``, ``lte``, ``in`` and ``range`` lookups. 449 456 450 For example:: 457 You may also want to implement this method to limit the lookup types that could 458 be used with your custom field type. 451 459 460 Note that, for ``range`` and ``in`` lookups, ``get_db_prep_lookup`` will receive 461 a list of objects (presumably of the right type) and will need to convert them 462 to a list of things of the right type for passing to the database. Most of the 463 time, you can reuse ``get_db_prep_value()``, or at least factor out some common 464 pieces. 465 466 For example, the following code implements ``get_db_prep_lookup`` to limit the 467 accepted lookup types to ``exact`` and ``in``:: 468 452 469 class HandField(models.Field): 453 470 # ... 454 471 455 472 def get_db_prep_lookup(self, lookup_type, value): 456 473 # We only handle 'exact' and 'in'. All others are errors. 457 474 if lookup_type == 'exact': 458 return self.get_db_prep_ save(value)475 return self.get_db_prep_value(value) 459 476 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] 461 478 else: 462 479 raise TypeError('Lookup type %r not supported.' % lookup_type) 463 480 … … 557 574 558 575 def flatten_data(self, follow, obj=None): 559 576 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)} 561 578 562 579 Some general advice 563 580 --------------------