Ticket #7560: 7560_get_db_prep_refactor.diff
File 7560_get_db_prep_refactor.diff, 30.7 KB (added by , 16 years ago) |
---|
-
django/db/backends/__init__.py
diff -r 403793bcdc1d -r 0fe9bb68aded django/db/backends/__init__.py
a b 4 4 except ImportError: 5 5 # Import copy of _thread_local.py from Python 2.4 6 6 from django.utils._threading_local import local 7 8 from django.db.backends import util 7 9 8 10 class BaseDatabaseWrapper(local): 9 11 """ … … 36 38 return cursor 37 39 38 40 def make_debug_cursor(self, cursor): 39 from django.db.backends import util40 41 return util.CursorDebugWrapper(cursor, self) 41 42 42 43 class BaseDatabaseFeatures(object): 43 44 allows_group_by_ordinal = True 44 45 inline_fk_references = True 45 needs_datetime_string_cast = True 46 needs_datetime_string_cast = True # uses 47 # django.db.backend.utils.typecast_timetamp 48 # on returned values from dates()? 46 49 supports_constraints = True 47 50 supports_tablespaces = False 48 51 uses_case_insensitive_names = False 49 52 uses_custom_query_class = False 50 53 empty_fetchmany_value = [] 51 54 update_can_self_select = True 52 supports_usecs = True53 time_field_needs_date = False54 55 interprets_empty_strings_as_nulls = False 55 date_field_supports_time_value = True56 56 can_use_chunked_reads = True 57 57 58 58 class BaseDatabaseOperations(object): … … 263 263 """Prepares a value for use in a LIKE query.""" 264 264 from django.utils.encoding import smart_unicode 265 265 return smart_unicode(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_") 266 267 def value_to_db_date(self, value): 268 """ 269 Transform a date value to an object compatible with what is expected 270 by the backend driver for date columns. 271 """ 272 if value is None: 273 return None 274 return value.strftime('%Y-%m-%d') 275 276 def value_to_db_datetime(self, value): 277 """ 278 Transform a datetime value to an object compatible with what is expected 279 by the backend driver for date columns. 280 """ 281 if value is None: 282 return None 283 return unicode(value) # XXX: Why not just str(value)? 284 285 def value_to_db_time(self, value): 286 """ 287 Transform a datetime value to an object compatible with what is expected 288 by the backend driver for date columns. 289 """ 290 if value is None: 291 return None 292 return unicode(value) # XXX: Why not just str(value)? 293 294 def value_to_db_decimal(self, value, max_digits, decimal_places): 295 """ 296 Transform a decimal.Decimal value to an object compatible with what is 297 expected by the backend driver for decimal (numeric) columns. 298 """ 299 if value is None: 300 return None 301 return util.format_number(value, max_digits, decimal_places) 302 303 def year_lookup_bounds(self, value): 304 """ 305 Returns a two-elements list with the lower and upper bound to be used 306 with a BETWEEN operator to query a field value using a year lookup 307 308 `value` is an int, containing the looked-up year. 309 """ 310 first = '%s-01-01 00:00:00' 311 second = '%s-12-31 23:59:59.999999' 312 return [first % value, second % value] 313 314 def year_lookup_bounds_for_date_field(self, value): 315 """ 316 Returns a two-elements list with the lower and upper bound to be used 317 with a BETWEEN operator to query a DateField value using a year lookup 318 319 `value` is an int, containing the looked-up year. 320 321 By default, it just calls `self.year_lookup_bounds`. Some backends need 322 this hook because on their DB date fields can't be compared to values 323 which include a time part. 324 """ 325 return self.year_lookup_bounds(value) 326 -
django/db/backends/mysql/base.py
diff -r 403793bcdc1d -r 0fe9bb68aded django/db/backends/mysql/base.py
a b 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): … … 123 122 return sql 124 123 else: 125 124 return [] 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] 126 145 127 146 class DatabaseWrapper(BaseDatabaseWrapper): 128 147 features = DatabaseFeatures() -
django/db/backends/mysql_old/base.py
diff -r 403793bcdc1d -r 0fe9bb68aded django/db/backends/mysql_old/base.py
a b 67 67 inline_fk_references = False 68 68 empty_fetchmany_value = () 69 69 update_can_self_select = False 70 supports_usecs = False71 70 72 71 class DatabaseOperations(BaseDatabaseOperations): 73 72 def date_extract_sql(self, lookup_type, field_name): … … 127 126 return sql 128 127 else: 129 128 return [] 129 130 def value_to_db_datetime(self, value): 131 # MySQL doesn't support microseconds 132 if value is None: 133 return None 134 # XXX: Why not just str(value)? 135 return unicode(value.replace(microsecond=0)) 136 137 def value_to_db_time(self, value): 138 # MySQL doesn't support microseconds 139 if value is None: 140 return None 141 # XXX: Why not just str(value)? 142 return unicode(value.replace(microsecond=0)) 143 144 145 def year_lookup_bounds(self, value): 146 # Again, no microseconds 147 first = '%s-01-01 00:00:00' 148 second = '%s-12-31 23:59:59.99' 149 return [first % value, second % value] 150 130 151 131 152 class DatabaseWrapper(BaseDatabaseWrapper): 132 153 features = DatabaseFeatures() -
django/db/backends/oracle/base.py
diff -r 403793bcdc1d -r 0fe9bb68aded django/db/backends/oracle/base.py
a b 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 … … 29 31 supports_tablespaces = True 30 32 uses_case_insensitive_names = True 31 33 uses_custom_query_class = True 32 time_field_needs_date = True33 34 interprets_empty_strings_as_nulls = True 34 date_field_supports_time_value = False35 35 36 36 class DatabaseOperations(BaseDatabaseOperations): 37 37 def autoinc_sql(self, table, column): … … 180 180 181 181 def tablespace_sql(self, tablespace, inline=False): 182 182 return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace)) 183 184 def value_to_db_time(self, value): 185 if value is None: 186 return None 187 if isinstance(value, basestring): 188 return datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6])) 189 return datetime.datetime(1900, 1, 1, value.hour, value.minute, 190 value.second, value.microsecond) 191 192 def year_lookup_bounds_for_date_field(self, value): 193 first = '%s-01-01' 194 second = '%s-12-31' 195 return [first % value, second % value] 196 197 183 198 184 199 class DatabaseWrapper(BaseDatabaseWrapper): 185 200 features = DatabaseFeatures() -
django/db/backends/sqlite3/base.py
diff -r 403793bcdc1d -r 0fe9bb68aded django/db/backends/sqlite3/base.py
a b 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() … … 156 162 dt = util.typecast_timestamp(dt) 157 163 except (ValueError, TypeError): 158 164 return None 159 return str(getattr(dt, lookup_type))165 return getattr(dt, lookup_type) 160 166 161 167 def _sqlite_date_trunc(lookup_type, dt): 162 168 try: -
django/db/backends/util.py
diff -r 403793bcdc1d -r 0fe9bb68aded django/db/backends/util.py
a b 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/models/fields/__init__.py
diff -r 403793bcdc1d -r 0fe9bb68aded django/db/models/fields/__init__.py
a b 224 224 "Returns field's value just before saving." 225 225 return getattr(model_instance, self.attname) 226 226 227 def get_db_prep_value(self, value): 228 """Returns field's value prepared for interacting with the database 229 backend. 230 231 Used by the default implementations of ``get_db_prep_save``and 232 `get_db_prep_lookup``` 233 """ 234 return value 235 227 236 def get_db_prep_save(self, value): 228 237 "Returns field's value prepared for saving into a database." 229 return value238 return self.get_db_prep_value(value) 230 239 231 240 def get_db_prep_lookup(self, lookup_type, value): 232 241 "Returns field's value prepared for database lookup." 233 242 if hasattr(value, 'as_sql'): 234 243 sql, params = value.as_sql() 235 244 return QueryWrapper(('(%s)' % sql), params) 236 if lookup_type in (' exact', 'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'):245 if lookup_type in ('regex', 'iregex', 'month', 'day', 'search'): 237 246 return [value] 247 elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'): 248 return [self.get_db_prep_value(value)] 238 249 elif lookup_type in ('range', 'in'): 239 return value250 return [self.get_db_prep_value(v) for v in value] 240 251 elif lookup_type in ('contains', 'icontains'): 241 252 return ["%%%s%%" % connection.ops.prep_for_like_query(value)] 242 253 elif lookup_type == 'iexact': … … 252 263 value = int(value) 253 264 except ValueError: 254 265 raise ValueError("The __year lookup type requires an integer argument") 255 if settings.DATABASE_ENGINE == 'sqlite3': 256 first = '%s-01-01' 257 second = '%s-12-31 23:59:59.999999' 258 elif not connection.features.date_field_supports_time_value and self.get_internal_type() == 'DateField': 259 first = '%s-01-01' 260 second = '%s-12-31' 261 elif not connection.features.supports_usecs: 262 first = '%s-01-01 00:00:00' 263 second = '%s-12-31 23:59:59.99' 266 267 if self.get_internal_type() == 'DateField': 268 return connection.ops.year_lookup_bounds_for_date_field(value) 264 269 else: 265 first = '%s-01-01 00:00:00' 266 second = '%s-12-31 23:59:59.999999' 267 return [first % value, second % value] 270 return connection.ops.year_lookup_bounds(value) 271 268 272 raise TypeError("Field has invalid lookup: %s" % lookup_type) 269 273 270 274 def has_default(self): … … 453 457 except (TypeError, ValueError): 454 458 raise validators.ValidationError, _("This value must be an integer.") 455 459 460 def get_db_prep_value(self, value): 461 if value is None: 462 return None 463 return int(value) 464 456 465 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): 457 466 if not rel: 458 467 return [] # Don't add a FormField unless it's in a related context. … … 491 500 if value in ('t', 'True', '1'): return True 492 501 if value in ('f', 'False', '0'): return False 493 502 raise validators.ValidationError, _("This value must be either True or False.") 503 504 def get_db_prep_value(self, value): 505 if value is None: 506 return None 507 return bool(value) 494 508 495 509 def get_manipulator_field_objs(self): 496 510 return [oldforms.CheckboxField] … … 553 567 except ValueError: 554 568 raise validators.ValidationError, _('Enter a valid date in YYYY-MM-DD format.') 555 569 556 def get_db_prep_lookup(self, lookup_type, value):557 if lookup_type in ('range', 'in'):558 value = [smart_unicode(v) for v in value]559 elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte') and hasattr(value, 'strftime'):560 value = value.strftime('%Y-%m-%d')561 else:562 value = smart_unicode(value)563 return Field.get_db_prep_lookup(self, lookup_type, value)564 565 570 def pre_save(self, model_instance, add): 566 571 if self.auto_now or (self.auto_now_add and add): 567 572 value = datetime.datetime.now() … … 585 590 else: 586 591 return self.editable or self.auto_now or self.auto_now_add 587 592 588 def get_db_prep_save(self, value): 589 # Casts dates into string format for entry into database. 590 if value is not None: 591 try: 592 value = value.strftime('%Y-%m-%d') 593 except AttributeError: 594 # If value is already a string it won't have a strftime method, 595 # so we'll just let it pass through. 596 pass 597 return Field.get_db_prep_save(self, value) 593 def get_db_prep_value(self, value): 594 # Casts dates into the format expected by the backend 595 return connection.ops.value_to_db_date(self.to_python(value)) 598 596 599 597 def get_manipulator_field_objs(self): 600 598 return [oldforms.DateField] … … 619 617 return value 620 618 if isinstance(value, datetime.date): 621 619 return datetime.datetime(value.year, value.month, value.day) 620 621 # Attempt to parse a datetime: 622 value = smart_str(value) 623 # split usecs, because they are not recognized by strptime. 624 if '.' in value: 625 try: 626 value, usecs = value.split('.') 627 usecs = int(usecs) 628 except ValueError: 629 raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM[ss[.uuuuuu]] format.') 630 else: 631 usecs = 0 632 kwargs = {'microsecond': usecs} 622 633 try: # Seconds are optional, so try converting seconds first. 623 return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6]) 634 return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6], 635 **kwargs) 636 624 637 except ValueError: 625 638 try: # Try without seconds. 626 return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M')[:5]) 639 return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M')[:5], 640 **kwargs) 627 641 except ValueError: # Try without hour/minutes/seconds. 628 642 try: 629 return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3]) 643 return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3], 644 **kwargs) 630 645 except ValueError: 631 raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM format.')646 raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM[ss[.uuuuuu]] format.') 632 647 633 def get_db_prep_save(self, value): 634 # Casts dates into string format for entry into database. 635 if value is not None: 636 # MySQL will throw a warning if microseconds are given, because it 637 # doesn't support microseconds. 638 if not connection.features.supports_usecs and hasattr(value, 'microsecond'): 639 value = value.replace(microsecond=0) 640 value = smart_unicode(value) 641 return Field.get_db_prep_save(self, value) 642 643 def get_db_prep_lookup(self, lookup_type, value): 644 if lookup_type in ('range', 'in'): 645 value = [smart_unicode(v) for v in value] 646 else: 647 value = smart_unicode(value) 648 return Field.get_db_prep_lookup(self, lookup_type, value) 648 def get_db_prep_value(self, value): 649 # Casts dates into the format expected by the backend 650 return connection.ops.value_to_db_datetime(self.to_python(value)) 649 651 650 652 def get_manipulator_field_objs(self): 651 653 return [oldforms.DateField, oldforms.TimeField] … … 705 707 Formats a number into a string with the requisite number of digits and 706 708 decimal places. 707 709 """ 708 num_chars = self.max_digits 709 # Allow for a decimal point 710 if self.decimal_places > 0: 711 num_chars += 1 712 # Allow for a minus sign 713 if value < 0: 714 num_chars += 1 710 # Method moved to django.db.backends.util. 711 # 712 # It is preserved because it is used by the oracle backend 713 # (django.db.backends.oracle.query), and also for 714 # backwards-compatibility with any external code which may have used 715 # this method. 716 from django.db.backends import util 717 return util.format_number(value, self.max_digits, self.decimal_places) 715 718 716 return u"%.*f" % (self.decimal_places, value) 717 718 def get_db_prep_save(self, value): 719 value = self._format(value) 720 return super(DecimalField, self).get_db_prep_save(value) 721 722 def get_db_prep_lookup(self, lookup_type, value): 723 if lookup_type in ('range', 'in'): 724 value = [self._format(v) for v in value] 725 else: 726 value = self._format(value) 727 return super(DecimalField, self).get_db_prep_lookup(lookup_type, value) 719 def get_db_prep_value(self, value): 720 return connection.ops.value_to_db_decimal(value, self.max_digits, 721 self.decimal_places) 728 722 729 723 def get_manipulator_field_objs(self): 730 724 return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)] … … 763 757 def get_internal_type(self): 764 758 return "FileField" 765 759 766 def get_db_prep_ save(self, value):760 def get_db_prep_value(self, value): 767 761 "Returns field's value prepared for saving into a database." 768 762 # Need to convert UploadedFile objects provided via a form to unicode for database insertion 769 763 if hasattr(value, 'name'): … … 904 898 class FloatField(Field): 905 899 empty_strings_allowed = False 906 900 901 def get_db_prep_value(self, value): 902 if value is None: 903 return None 904 return float(value) 905 907 906 def get_manipulator_field_objs(self): 908 907 return [oldforms.FloatField] 909 908 … … 954 953 955 954 class IntegerField(Field): 956 955 empty_strings_allowed = False 956 def get_db_prep_value(self, value): 957 if value is None: 958 return None 959 return int(value) 960 957 961 def get_manipulator_field_objs(self): 958 962 return [oldforms.IntegerField] 959 963 … … 1001 1005 if value in ('f', 'False', '0'): return False 1002 1006 raise validators.ValidationError, _("This value must be either None, True or False.") 1003 1007 1008 def get_db_prep_value(self, value): 1009 if value is None: 1010 return None 1011 return bool(value) 1012 1004 1013 def get_manipulator_field_objs(self): 1005 1014 return [oldforms.NullBooleanField] 1006 1015 … … 1009 1018 defaults.update(kwargs) 1010 1019 return super(NullBooleanField, self).formfield(**defaults) 1011 1020 1012 class PhoneNumberField( IntegerField):1021 class PhoneNumberField(Field): 1013 1022 def get_manipulator_field_objs(self): 1014 1023 return [oldforms.PhoneNumberField] 1015 1024 … … 1091 1100 def get_internal_type(self): 1092 1101 return "TimeField" 1093 1102 1094 def get_db_prep_lookup(self, lookup_type, value): 1095 if connection.features.time_field_needs_date: 1096 # Oracle requires a date in order to parse. 1097 def prep(value): 1098 if isinstance(value, datetime.time): 1099 value = datetime.datetime.combine(datetime.date(1900, 1, 1), value) 1100 return smart_unicode(value) 1103 def to_python(self, value): 1104 if value is None: 1105 return None 1106 if isinstance(value, datetime.time): 1107 return value 1108 1109 # Attempt to parse a datetime: 1110 value = smart_str(value) 1111 # split usecs, because they are not recognized by strptime. 1112 if '.' in value: 1113 try: 1114 value, usecs = value.split('.') 1115 usecs = int(usecs) 1116 except ValueError: 1117 raise validators.ValidationError, _('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.') 1101 1118 else: 1102 prep = smart_unicode 1103 if lookup_type in ('range', 'in'): 1104 value = [prep(v) for v in value] 1105 else: 1106 value = prep(value) 1107 return Field.get_db_prep_lookup(self, lookup_type, value) 1119 usecs = 0 1120 kwargs = {'microsecond': usecs} 1121 1122 try: # Seconds are optional, so try converting seconds first. 1123 return datetime.time(*time.strptime(value, '%H:%M:%S')[3:6], 1124 **kwargs) 1125 except ValueError: 1126 try: # Try without seconds. 1127 return datetime.datetime(*time.strptime(value, '%H:%M')[:5], 1128 **kwargs) 1129 except ValueError: 1130 raise validators.ValidationError, _('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.') 1108 1131 1109 1132 def pre_save(self, model_instance, add): 1110 1133 if self.auto_now or (self.auto_now_add and add): … … 1114 1137 else: 1115 1138 return super(TimeField, self).pre_save(model_instance, add) 1116 1139 1117 def get_db_prep_save(self, value): 1118 # Casts dates into string format for entry into database. 1119 if value is not None: 1120 # MySQL will throw a warning if microseconds are given, because it 1121 # doesn't support microseconds. 1122 if not connection.features.supports_usecs and hasattr(value, 'microsecond'): 1123 value = value.replace(microsecond=0) 1124 if connection.features.time_field_needs_date: 1125 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 1126 if isinstance(value, datetime.time): 1127 value = datetime.datetime(1900, 1, 1, value.hour, value.minute, 1128 value.second, value.microsecond) 1129 elif isinstance(value, basestring): 1130 value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6])) 1131 else: 1132 value = smart_unicode(value) 1133 return Field.get_db_prep_save(self, value) 1140 def get_db_prep_value(self, value): 1141 # Casts times into the format expected by the backend 1142 return connection.ops.value_to_db_time(self.to_python(value)) 1134 1143 1135 1144 def get_manipulator_field_objs(self): 1136 1145 return [oldforms.TimeField] -
docs/custom_model_fields.txt
diff -r 403793bcdc1d -r 0fe9bb68aded docs/custom_model_fields.txt
a b 386 386 called when it is created, you should be using `The SubfieldBase metaclass`_ 387 387 mentioned earlier. Otherwise ``to_python()`` won't be called automatically. 388 388 389 ``get_db_prep_ save(self, value)``390 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 389 ``get_db_prep_value(self, value)`` 390 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 391 391 392 392 This is the reverse of ``to_python()`` when working with the database backends 393 393 (as opposed to serialization). The ``value`` parameter is the current value of … … 400 400 class HandField(models.Field): 401 401 # ... 402 402 403 def get_db_prep_ save(self, value):403 def get_db_prep_value(self, value): 404 404 return ''.join([''.join(l) for l in (value.north, 405 405 value.east, value.south, value.west)]) 406 407 ``get_db_prep_save(self, value)`` 408 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 409 410 Same as the above, but called when the Field value must be *saved* to the 411 database. As the default implementation just calls ``get_db_prep_value``, you 412 shouldn't need to implement this method unless your custom field need a special 413 conversion when being saved that is not the same as the used for normal query 414 parameters (which is implemented by ``get_db_prep_value``). 415 406 416 407 417 ``pre_save(self, model_instance, add)`` 408 418 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ … … 441 451 and pass the rest of the ``get_db_prep_lookup()`` method of the parent class. 442 452 443 453 If you needed to implement ``get_db_prep_save()``, you will usually need to 444 implement ``get_db_prep_lookup()``. The usual reason is because of the 445 ``range`` and ``in`` lookups. In these case, you will passed a list of 446 objects (presumably of the right type) and will need to convert them to a list 447 of things of the right type for passing to the database. Sometimes you can 448 reuse ``get_db_prep_save()``, or at least factor out some common pieces from 449 both methods into a help function. 454 implement ``get_db_prep_lookup()``. If you don't, ``get_db_prep_value`` will be 455 called by the default implementation, to manage ``exact``, ``gt``, ``gte``, 456 ``lt``, ``lte``, ``in`` and ``range`` lookups. 450 457 451 For example:: 458 You may also want to implement this method to limit the lookup types that could 459 be used with your custom field type. 460 461 Note that, for ``range`` and ``in`` lookups, ``get_db_prep_lookup`` will receive 462 a list of objects (presumably of the right type) and will need to convert them 463 to a list of things of the right type for passing to the database. Most of the 464 time, you can reuse ``get_db_prep_value()``, or at least factor out some common 465 pieces. 466 467 For example, the following code implements ``get_db_prep_lookup`` to limit the 468 accepted lookup types to ``exact`` and ``in``:: 452 469 453 470 class HandField(models.Field): 454 471 # ... … … 456 473 def get_db_prep_lookup(self, lookup_type, value): 457 474 # We only handle 'exact' and 'in'. All others are errors. 458 475 if lookup_type == 'exact': 459 return self.get_db_prep_ save(value)476 return self.get_db_prep_value(value) 460 477 elif lookup_type == 'in': 461 return [self.get_db_prep_ save(v) for v in value]478 return [self.get_db_prep_value(v) for v in value] 462 479 else: 463 480 raise TypeError('Lookup type %r not supported.' % lookup_type) 464 481 … … 558 575 559 576 def flatten_data(self, follow, obj=None): 560 577 value = self._get_val_from_obj(obj) 561 return {self.attname: self.get_db_prep_ save(value)}578 return {self.attname: self.get_db_prep_value(value)} 562 579 563 580 Some general advice 564 581 -------------------- -
tests/modeltests/custom_methods/models.py
diff -r 403793bcdc1d -r 0fe9bb68aded tests/modeltests/custom_methods/models.py
a b 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_fields/tests.py
diff -r 403793bcdc1d -r 0fe9bb68aded tests/regressiontests/model_fields/tests.py
a b 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 """ -
tests/regressiontests/model_regress/models.py
diff -r 403793bcdc1d -r 0fe9bb68aded tests/regressiontests/model_regress/models.py
a b 28 28 29 29 class Party(models.Model): 30 30 when = models.DateField() 31 32 class Event(models.Model): 33 when = models.DateTimeField() 31 34 32 35 __test__ = {'API_TESTS': """ 33 36 (NOTE: Part of the regression test here is merely parsing the model … … 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 }