Ticket #7560: get_db_prep_refactor-3.patch
File get_db_prep_refactor-3.patch, 23.7 KB (added by , 16 years ago) |
---|
-
django/db/backends/__init__.py
diff -r 7dfb4f5c1bb0 django/db/backends/__init__.py
a b except ImportError: 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 """ … … class BaseDatabaseWrapper(local): 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 57 57 class BaseDatabaseOperations(object): 58 58 """ … … class BaseDatabaseOperations(object): 272 272 """Prepares a value for use in a LIKE query.""" 273 273 from django.utils.encoding import smart_unicode 274 274 return smart_unicode(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_") 275 276 def value_to_db_date(self, value): 277 """ 278 Transform a date 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 value.strftime('%Y-%m-%d') 284 285 def value_to_db_datetime(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_time(self, value): 295 """ 296 Transform a datetime value to an object compatible with what is expected 297 by the backend driver for date columns. 298 """ 299 if value is None: 300 return None 301 return unicode(value) # XXX: Why not just str(value)? 302 303 def value_to_db_decimal(self, value, max_digits, decimal_places): 304 """ 305 Transform a decimal.Decimal value to an object compatible with what is 306 expected by the backend driver for decimal (numeric) columns. 307 """ 308 if value is None: 309 return None 310 return util.format_number(value, max_digits, decimal_places) 311 312 def year_lookup_bounds(self, value): 313 """ 314 Returns a two-elements list with the lower and upper bound to be used 315 with a BETWEEN operator to query a field value using a year lookup 316 317 `value` is an int, containing the looked-up year. 318 """ 319 first = '%s-01-01 00:00:00' 320 second = '%s-12-31 23:59:59.999999' 321 return [first % value, second % value] 322 323 def year_lookup_bounds_for_date_field(self, value): 324 """ 325 Returns a two-elements list with the lower and upper bound to be used 326 with a BETWEEN operator to query a DateField value using a year lookup 327 328 `value` is an int, containing the looked-up year. 329 330 By default, it just calls `self.year_lookup_bounds`. Some backends need 331 this hook because on their DB date fields can't be compared to values 332 which include a time part. 333 """ 334 return self.year_lookup_bounds(value) 335 -
django/db/backends/mysql/base.py
diff -r 7dfb4f5c1bb0 django/db/backends/mysql/base.py
a b class DatabaseFeatures(BaseDatabaseFeatu 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): … … class DatabaseOperations(BaseDatabaseOpe 130 129 return sql 131 130 else: 132 131 return [] 132 133 def value_to_db_datetime(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 value_to_db_time(self, value): 141 # MySQL doesn't support microseconds 142 if value is None: 143 return None 144 # XXX: Why not just str(value)? 145 return unicode(value.replace(microsecond=0)) 146 147 def year_lookup_bounds(self, value): 148 # Again, no microseconds 149 first = '%s-01-01 00:00:00' 150 second = '%s-12-31 23:59:59.99' 151 return [first % value, second % value] 133 152 134 153 class DatabaseWrapper(BaseDatabaseWrapper): 135 154 features = DatabaseFeatures() -
django/db/backends/mysql_old/base.py
diff -r 7dfb4f5c1bb0 django/db/backends/mysql_old/base.py
a b class DatabaseFeatures(BaseDatabaseFeatu 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): … … class DatabaseOperations(BaseDatabaseOpe 134 133 return sql 135 134 else: 136 135 return [] 136 137 def value_to_db_datetime(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 def value_to_db_time(self, value): 145 # MySQL doesn't support microseconds 146 if value is None: 147 return None 148 # XXX: Why not just str(value)? 149 return unicode(value.replace(microsecond=0)) 150 151 152 def year_lookup_bounds(self, value): 153 # Again, no microseconds 154 first = '%s-01-01 00:00:00' 155 second = '%s-12-31 23:59:59.99' 156 return [first % value, second % value] 157 137 158 138 159 class DatabaseWrapper(BaseDatabaseWrapper): 139 160 features = DatabaseFeatures() -
django/db/backends/oracle/base.py
diff -r 7dfb4f5c1bb0 django/db/backends/oracle/base.py
a b Requires cx_Oracle: http://www.python.ne 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 … … class DatabaseFeatures(BaseDatabaseFeatu 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): … … class DatabaseOperations(BaseDatabaseOpe 185 185 186 186 def tablespace_sql(self, tablespace, inline=False): 187 187 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) 196 197 def year_lookup_bounds_for_date_field(self, value): 198 first = '%s-01-01' 199 second = '%s-12-31' 200 return [first % value, second % value] 201 202 188 203 189 204 class DatabaseWrapper(BaseDatabaseWrapper): 190 205 features = DatabaseFeatures() -
django/db/backends/sqlite3/base.py
diff -r 7dfb4f5c1bb0 django/db/backends/sqlite3/base.py
a b class DatabaseOperations(BaseDatabaseOpe 79 79 # sql_flush() implementations). Just return SQL at this point 80 80 return sql 81 81 82 def year_lookup_bounds(self, value): 83 first = '%s-01-01' 84 second = '%s-12-31 23:59:59.999999' 85 return [first % value, second % value] 86 87 82 88 class DatabaseWrapper(BaseDatabaseWrapper): 83 89 features = DatabaseFeatures() 84 90 ops = DatabaseOperations() … … def _sqlite_extract(lookup_type, dt): 150 156 try: 151 157 dt = util.typecast_timestamp(dt) 152 158 except (ValueError, TypeError): 159 print "error!" 153 160 return None 154 return str(getattr(dt, lookup_type))161 return getattr(dt, lookup_type) 155 162 156 163 def _sqlite_date_trunc(lookup_type, dt): 157 164 try: -
django/db/backends/util.py
diff -r 7dfb4f5c1bb0 django/db/backends/util.py
a b def truncate_name(name, length=None): 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 7dfb4f5c1bb0 django/db/models/fields/__init__.py
a b class Field(object): 252 252 value = int(value) 253 253 except ValueError: 254 254 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' 255 256 if self.get_internal_type() == 'DateField': 257 return connection.ops.year_lookup_bounds_for_date_field(value) 264 258 else: 265 first = '%s-01-01 00:00:00' 266 second = '%s-12-31 23:59:59.999999' 267 return [first % value, second % value] 259 return connection.ops.year_lookup_bounds(value) 260 268 261 raise TypeError("Field has invalid lookup: %s" % lookup_type) 269 262 270 263 def has_default(self): … … class AutoField(Field): 453 446 except (TypeError, ValueError): 454 447 raise validators.ValidationError, _("This value must be an integer.") 455 448 449 def get_db_prep_save(self, value): 450 if value is None: 451 return None 452 return int(value) 453 454 def get_db_prep_lookup(self, lookup_type, value): 455 if lookup_type == 'range': 456 value = [int(v) for v in value] 457 elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'): 458 value = int(value) 459 return Field.get_db_prep_lookup(self, lookup_type, value) 460 456 461 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): 457 462 if not rel: 458 463 return [] # Don't add a FormField unless it's in a related context. … … class BooleanField(Field): 491 496 if value in ('t', 'True', '1'): return True 492 497 if value in ('f', 'False', '0'): return False 493 498 raise validators.ValidationError, _("This value must be either True or False.") 499 500 def get_db_prep_save(self, value): 501 if value is None: 502 return None 503 return bool(value) 504 505 def get_db_prep_lookup(self, lookup_type, value): 506 if lookup_type == 'exact' and value is not None: 507 value = bool(value) 508 return Field.get_db_prep_lookup(self, lookup_type, value) 494 509 495 510 def get_manipulator_field_objs(self): 496 511 return [oldforms.CheckboxField] … … class DateField(Field): 554 569 raise validators.ValidationError, _('Enter a valid date in YYYY-MM-DD format.') 555 570 556 571 def get_db_prep_lookup(self, lookup_type, value): 572 value_to_db_date = connection.ops.value_to_db_date 557 573 if lookup_type == 'range': 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) 574 value = [value_to_db_date(self.to_python(v)) for v in value] 575 elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'): 576 # Lookups with a date argument 577 value = value_to_db_date(self.to_python(value)) 563 578 return Field.get_db_prep_lookup(self, lookup_type, value) 564 579 565 580 def pre_save(self, model_instance, add): … … class DateField(Field): 586 601 return self.editable or self.auto_now or self.auto_now_add 587 602 588 603 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) 604 # Casts dates into the format expected by the backend 605 return connection.ops.value_to_db_date(self.to_python(value)) 598 606 599 607 def get_manipulator_field_objs(self): 600 608 return [oldforms.DateField] … … class DateTimeField(DateField): 631 639 raise validators.ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM format.') 632 640 633 641 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 # Casts dates into the format expected by the backend 643 return connection.ops.value_to_db_datetime(self.to_python(value)) 642 644 643 645 def get_db_prep_lookup(self, lookup_type, value): 646 value_to_db_datetime = connection.ops.value_to_db_datetime 644 647 if lookup_type == 'range': 645 value = [ smart_unicode(v) for v in value]646 el se:647 value = smart_unicode(value)648 value = [value_to_db_datetime(self.to_python(v)) for v in value] 649 elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'): 650 value = value_to_db_datetime(self.to_python(value)) 648 651 return Field.get_db_prep_lookup(self, lookup_type, value) 649 652 650 653 def get_manipulator_field_objs(self): … … class DecimalField(Field): 705 708 Formats a number into a string with the requisite number of digits and 706 709 decimal places. 707 710 """ 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 715 716 return u"%.*f" % (self.decimal_places, value) 711 # Method moved to django.db.backends.util. 712 # 713 # It is preserved because it is used by the oracle backend 714 # (django.db.backends.oracle.query), and also for 715 # backwards-compatibility with any external code which may have used 716 # this method. 717 from django.db.backends import util 718 return util.format_number(value, self.max_digits, self.decimal_places) 717 719 718 720 def get_db_prep_save(self, value): 719 value = self._format(value)720 return super(DecimalField, self).get_db_prep_save(value)721 return connection.ops.value_to_db_decimal(value, self.max_digits, 722 self.decimal_places) 721 723 722 724 def get_db_prep_lookup(self, lookup_type, value): 725 value_to_db_decimal = connection.ops.value_to_db_decimal 723 726 if lookup_type == 'range': 724 value = [self._format(v) for v in value] 727 value = [value_to_db_decimal(v, self.max_digits, 728 self.decimal_places) 729 for v in value] 725 730 else: 726 value = self._format(value) 731 value = value_to_db_decimal(value, self.max_digits, 732 self.decimal_places) 727 733 return super(DecimalField, self).get_db_prep_lookup(lookup_type, value) 728 734 729 735 def get_manipulator_field_objs(self): … … class FloatField(Field): 898 904 class FloatField(Field): 899 905 empty_strings_allowed = False 900 906 907 def get_db_prep_save(self, value): 908 if value is None: 909 return None 910 return float(value) 911 912 def get_db_prep_lookup(self, lookup_type, value): 913 if lookup_type == 'range': 914 value = [float(v) for v in value] 915 elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'): 916 value = float(value) 917 return Field.get_db_prep_lookup(self, lookup_type, value) 918 901 919 def get_manipulator_field_objs(self): 902 920 return [oldforms.FloatField] 903 921 … … class ImageField(FileField): 948 966 949 967 class IntegerField(Field): 950 968 empty_strings_allowed = False 969 def get_db_prep_save(self, value): 970 if value is None: 971 return None 972 return int(value) 973 974 def get_db_prep_lookup(self, lookup_type, value): 975 if lookup_type == 'range': 976 value = [int(v) for v in value] 977 elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'): 978 value = int(value) 979 return Field.get_db_prep_lookup(self, lookup_type, value) 980 951 981 def get_manipulator_field_objs(self): 952 982 return [oldforms.IntegerField] 953 983 … … class NullBooleanField(Field): 995 1025 if value in ('f', 'False', '0'): return False 996 1026 raise validators.ValidationError, _("This value must be either None, True or False.") 997 1027 1028 def get_db_prep_save(self, value): 1029 if value is None: 1030 return None 1031 return bool(value) 1032 1033 def get_db_prep_lookup(self, lookup_type, value): 1034 if lookup_type == 'exact' and value is not None: 1035 value = bool(value) 1036 return Field.get_db_prep_lookup(self, lookup_type, value) 1037 1038 998 1039 def get_manipulator_field_objs(self): 999 1040 return [oldforms.NullBooleanField] 1000 1041 … … class NullBooleanField(Field): 1003 1044 defaults.update(kwargs) 1004 1045 return super(NullBooleanField, self).formfield(**defaults) 1005 1046 1006 class PhoneNumberField( IntegerField):1047 class PhoneNumberField(Field): 1007 1048 def get_manipulator_field_objs(self): 1008 1049 return [oldforms.PhoneNumberField] 1009 1050 … … class TimeField(Field): 1085 1126 def get_internal_type(self): 1086 1127 return "TimeField" 1087 1128 1129 def to_python(self, value): 1130 if value is None: 1131 return None 1132 if isinstance(value, datetime.time): 1133 return value 1134 try: # Seconds are optional, so try converting seconds first. 1135 return datetime.time(*time.strptime(value, '%H:%M:%S')[3:6]) 1136 except ValueError: 1137 try: # Try without seconds. 1138 return datetime.datetime(*time.strptime(value, '%H:%M')[:5]) 1139 except ValueError: 1140 raise validators.ValidationError, _('Enter a valid time in HH:MM[:ss] format.') 1141 1088 1142 def get_db_prep_lookup(self, lookup_type, value): 1089 if connection.features.time_field_needs_date: 1090 # Oracle requires a date in order to parse. 1091 def prep(value): 1092 if isinstance(value, datetime.time): 1093 value = datetime.datetime.combine(datetime.date(1900, 1, 1), value) 1094 return smart_unicode(value) 1095 else: 1096 prep = smart_unicode 1143 value_to_db_time = connection.ops.value_to_db_time 1097 1144 if lookup_type == 'range': 1098 value = [ prep(v) for v in value]1099 el se:1100 value = prep(value)1145 value = [value_to_db_time(self.to_python(v)) for v in value] 1146 elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'): 1147 value = value_to_db_time(self.to_python(value)) 1101 1148 return Field.get_db_prep_lookup(self, lookup_type, value) 1102 1149 1103 1150 def pre_save(self, model_instance, add): … … class TimeField(Field): 1109 1156 return super(TimeField, self).pre_save(model_instance, add) 1110 1157 1111 1158 def get_db_prep_save(self, value): 1112 # Casts dates into string format for entry into database. 1113 if value is not None: 1114 # MySQL will throw a warning if microseconds are given, because it 1115 # doesn't support microseconds. 1116 if not connection.features.supports_usecs and hasattr(value, 'microsecond'): 1117 value = value.replace(microsecond=0) 1118 if connection.features.time_field_needs_date: 1119 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 1120 if isinstance(value, datetime.time): 1121 value = datetime.datetime(1900, 1, 1, value.hour, value.minute, 1122 value.second, value.microsecond) 1123 elif isinstance(value, basestring): 1124 value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6])) 1125 else: 1126 value = smart_unicode(value) 1127 return Field.get_db_prep_save(self, value) 1159 # Casts times into the format expected by the backend 1160 return connection.ops.value_to_db_time(self.to_python(value)) 1128 1161 1129 1162 def get_manipulator_field_objs(self): 1130 1163 return [oldforms.TimeField] -
tests/modeltests/custom_methods/models.py
diff -r 7dfb4f5c1bb0 tests/modeltests/custom_methods/models.py
a b class Article(models.Model): 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 7dfb4f5c1bb0 tests/regressiontests/model_fields/tests.py
a b ValidationError: [u'This value must be a 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