Ticket #2443: durationfield.patch
File durationfield.patch, 9.8 KB (added by , 15 years ago) |
---|
-
django/db/models/fields/duration.py
1 # -*- coding: utf-8 -*- 2 from datetime import timedelta 3 from django.forms.fields import DurationField as FDurationField 4 from django.db.models.fields import Field 5 from django.core.exceptions import ValidationError 6 from django.db import connection 7 from django.utils import timestring 8 9 class DurationField(Field): 10 def __init__(self, *args, **kwargs): 11 super(DurationField, self).__init__(*args, **kwargs) 12 self.max_digits, self.decimal_places = 20, 6 13 14 def get_internal_type(self): 15 return "BigIntegerField" 16 17 def get_db_prep_save(self, value): 18 if value is None: 19 return None # db NULL 20 if isinstance(value, int) or isinstance(value, long): 21 value = timedelta(microseconds=value) 22 return value.days * 24 * 3600 * 1000000 + value.seconds * 1000000 + value.microseconds 23 24 def to_python(self, value): 25 return value 26 27 def formfield(self, form_class=FDurationField, **kwargs): 28 return super(DurationField, self).formfield(form_class, **kwargs) -
django/forms/fields.py
1 # -*- coding: utf-8 -*- 1 2 """ 2 3 Field classes. 3 4 """ … … 17 18 from django.core.exceptions import ValidationError 18 19 from django.core import validators 19 20 import django.utils.copycompat as copy 21 from django.utils.datastructures import SortedDict 20 22 from django.utils.translation import ugettext_lazy as _ 21 23 from django.utils.encoding import smart_unicode, smart_str 22 24 from django.utils.formats import get_format 23 25 from django.utils.functional import lazy 26 from django.utils.timestring import to_timedelta 24 27 25 28 # Provide this import for backwards compatibility. 26 29 from django.core.validators import EMPTY_VALUES 27 30 28 31 from util import ErrorList 29 32 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, \ 30 FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, \33 DurationInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, \ 31 34 DateInput, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget 32 35 33 36 __all__ = ( … … 841 844 return datetime.datetime.combine(*data_list) 842 845 return None 843 846 847 848 class DurationField(Field): 849 widget = DurationInput 850 default_error_messages = { 851 'invalid': u'Enter a valid duration.', 852 'max_value': _(u'Ensure this value is less than or equal to %(limit_value)s.'), 853 'min_value': _(u'Ensure this value is greater than or equal to %(limit_value)s.'), 854 } 844 855 856 def __init__(self, min_value=None, max_value=None, *args, **kwargs): 857 super(DurationField, self).__init__(*args, **kwargs) 858 859 if max_value is not None: 860 self.validators.append(validators.MaxValueValidator(max_value)) 861 if min_value is not None: 862 self.validators.append(validators.MinValueValidator(min_value)) 863 864 def clean(self, value): 865 """ 866 Validates max_value and min_value. 867 Returns a datetime.timedelta object. 868 """ 869 try: 870 return to_timedelta(value) 871 except ValueError, e: 872 raise ValidationError(e) 873 874 if self.max_value is not None and value > self.max_value: 875 raise ValidationError(self.error_messages['max_value'] % {'max': self.max_value}) 876 877 if self.min_value is not None and value < self.min_value: 878 raise ValidationError(self.error_messages['min_value'] % {'min': self.min_value}) 879 880 return value 881 882 def to_python(self, value): 883 try: 884 return to_timedelta(value) 885 except ValueError, e: 886 raise ValidationError(e) 887 888 845 889 class IPAddressField(CharField): 846 890 default_error_messages = { 847 891 'invalid': _(u'Enter a valid IPv4 address.'), -
django/forms/widgets.py
1 # -*- coding: utf-8 -*- 1 2 """ 2 3 HTML Widget classes 3 4 """ … … 10 11 from django.utils.translation import ugettext 11 12 from django.utils.encoding import StrAndUnicode, force_unicode 12 13 from django.utils.safestring import mark_safe 14 from django.utils.timestring import from_timedelta 13 15 from django.utils import datetime_safe, formats 14 from datetime import time 16 from datetime import time, timedelta 15 17 from util import flatatt 16 18 from urlparse import urljoin 17 19 … … 593 595 option_value = force_unicode(option_value) 594 596 rendered_cb = cb.render(name, option_value) 595 597 option_label = conditional_escape(force_unicode(option_label)) 596 output.append(u'<li> <label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label))598 output.append(u'<li>%(cb)s<label%(for)s>%(label)s</label></li>' % {"for": label_for, "label": option_label, "cb": rendered_cb}) 597 599 output.append(u'</ul>') 598 600 return mark_safe(u'\n'.join(output)) 599 601 … … 699 701 media = media + w.media 700 702 return media 701 703 media = property(_get_media) 704 705 class DurationInput(TextInput): 706 def render(self, name, value, attrs=None): 707 if value is None: value = '' 708 final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) 709 if value != '': 710 # Only add the 'value' attribute if a value is non-empty. 711 if isinstance(value, int) or isinstance(value, long): # Database backends serving different types 712 value = from_timedelta(timedelta(microseconds=value)) 713 final_attrs['value'] = force_unicode(formats.localize_input(value)) 714 return mark_safe(u'<input%s />' % flatatt(final_attrs)) 702 715 703 716 class SplitDateTimeWidget(MultiWidget): 704 717 """ -
django/shortcuts/__init__.py
1 # -*- coding: utf-8 -*- 1 2 """ 2 3 This module collects helper functions and classes that "span" multiple levels 3 4 of MVC. In other words, these functions/classes introduce controlled coupling … … 23 24 """ 24 25 Returns an HttpResponseRedirect to the apropriate URL for the arguments 25 26 passed. 26 27 27 28 The arguments could be: 28 29 29 30 * A model: the model's `get_absolute_url()` function will be called. 30 31 31 32 * A view name, possibly with arguments: `urlresolvers.reverse()` will 32 33 be used to reverse-resolve the name. 33 34 34 35 * A URL, which will be used as-is for the redirect location. 35 36 36 37 By default issues a temporary redirect; pass permanent=True to issue a 37 38 permanent redirect 38 39 """ … … 40 41 redirect_class = HttpResponsePermanentRedirect 41 42 else: 42 43 redirect_class = HttpResponseRedirect 43 44 44 45 # If it's a model, use get_absolute_url() 45 46 if hasattr(to, 'get_absolute_url'): 46 47 return redirect_class(to.get_absolute_url()) 47 48 48 49 # Next try a reverse URL resolution. 49 50 try: 50 51 return redirect_class(urlresolvers.reverse(to, args=args, kwargs=kwargs)) … … 55 56 # If this doesn't "feel" like a URL, re-raise. 56 57 if '/' not in to and '.' not in to: 57 58 raise 58 59 59 60 # Finally, fall back and assume it's a URL 60 61 return redirect_class(to) 61 62 -
django/utils/timestring.py
1 # -*- coding: utf-8 -*- 2 """ 3 Utility functions to convert back and forth between a timestring and timedelta. 4 1y 7m 6w 3d 18h 30min 23s 10ms 150mis 5 => 1 year 7 months 6 weeks 3 days 18 hours 30 minutes 23 seconds 10 milliseconds 150 microseconds 6 => datetime.timedelta(624, 6155, 805126) 7 """ 8 9 from datetime import timedelta 10 from django.utils.datastructures import SortedDict 11 12 values_in_microseconds = SortedDict(( 13 # Uncomment the following two lines for year and month support 14 # ('y', 31556925993600), # 52.177457 * (7*24*60*60*1000*1000) 15 # ('m', 2629743832800), # 4.34812141 * (7*24*60*60*1000*1000) 16 ('w', 604800000000), # 7*24*60*60*1000*1000 17 ('d', 86400000000), # 24*60*60*1000*1000 18 ('h', 3600000000), # 60*60*1000*1000 19 ('min', 60000000), # 60*1000*1000 20 ('s', 1000000), # 1000*1000 21 ('ms', 1000), 22 ('us', 1), 23 )) 24 25 def to_timedelta(value): 26 chunks = [] 27 for b in value.lower().split(): 28 for index, char in enumerate(b): 29 if not char.isdigit(): 30 chunks.append((b[:index], b[index:])) # digits, letters 31 break 32 33 microseconds = 0 34 for digits, chars in chunks: 35 if not digits or not chars in values_in_microseconds: 36 raise ValueError('Incorrect timestring pair') 37 microseconds += int(digits) * values_in_microseconds[chars] 38 39 return timedelta(microseconds=microseconds) 40 41 def from_timedelta(value): 42 if not value: 43 return u'0s' 44 45 if not isinstance(value, timedelta): 46 raise ValueError('to_timestring argument must be a datetime.timedelta instance') 47 48 chunks = [] 49 microseconds = value.days * 24 * 3600 * 1000000 + value.seconds * 1000000 + value.microseconds 50 for k in values_in_microseconds: 51 if microseconds >= values_in_microseconds[k]: 52 diff, microseconds = divmod(microseconds, values_in_microseconds[k]) 53 chunks.append('%d%s' % (diff, k)) 54 return u' '.join(chunks)