Django

Code

Ticket #2443: durationfield.7.diff

File durationfield.7.diff, 6.9 kB (added by Adys, 1 year ago)

Updated patch for current trunk, without docs (no idea how much they've changed)

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

    old new  
    636636        defaults.update(kwargs) 
    637637        return super(DecimalField, self).formfield(**defaults) 
    638638 
     639class DurationProxy(object): 
     640    def __init__(self, field): 
     641        self.field_name = field.name 
     642 
     643    def __get__(self, instance=None, owner=None): 
     644        if instance is None: 
     645            raise AttributeError, "%s can only be accessed from %s instances." % (self.field_name, owner.__name__) 
     646        if self.field_name not in instance.__dict__: 
     647            return None 
     648        return instance.__dict__[self.field_name] 
     649 
     650    def __set__(self, instance, value): 
     651        if value and not isinstance(value, datetime.timedelta): 
     652            value = datetime.timedelta(seconds=float(value)) 
     653        instance.__dict__[self.field_name] = value 
     654 
     655class DurationField(Field): 
     656    def __init__(self, *args, **kwargs): 
     657        super(DurationField, self).__init__(*args, **kwargs) 
     658        self.max_digits, self.decimal_places = 20, 6 
     659 
     660    def get_internal_type(self): 
     661        return "DecimalField" 
     662 
     663    def contribute_to_class(self, cls, name): 
     664        super(DurationField, self).contribute_to_class(cls, name) 
     665        setattr(cls, name, DurationProxy(self)) 
     666 
     667    def get_db_prep_save(self, value): 
     668        if value is None: 
     669            return None 
     670        return str(value.days * 24 * 3600 + value.seconds + float(value.microseconds) / 1000000) 
     671 
     672    def to_python(self, value): 
     673        if isinstance(value, datetime.timedelta): 
     674            return value 
     675        try: 
     676            return datetime.timedelta(seconds=float(value)) 
     677        except (TypeError, ValueError): 
     678            raise validators.ValidationError('This value must be a real number.') 
     679        except OverflowError: 
     680            raise validators.ValidationError('The maximum allowed value is %s' % datetime.timedelta.max) 
     681 
     682    def flatten_data(self, follow, obj=None): 
     683        val = self._get_val_from_obj(obj) 
     684        if val is None or val is '': 
     685            return '' 
     686        return {self.name: self.get_db_prep_save(val)} 
     687 
     688    def formfield(self, form_class=forms.DurationField, **kwargs): 
     689        return super(DurationField, self).formfield(form_class, **kwargs) 
     690 
     691    def get_manipulator_field_objs(self): 
     692        return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)] 
     693 
    639694class EmailField(CharField): 
    640695    def __init__(self, *args, **kwargs): 
    641696        kwargs['max_length'] = kwargs.get('max_length', 75) 
  • django/forms/fields.py

    old new  
    2828from django.utils.encoding import smart_unicode, smart_str 
    2929 
    3030from util import ErrorList, ValidationError 
    31 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget 
     31from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput, DurationWidget, SplitDateTimeWidget, SplitHiddenDateTimeWidget 
    3232from django.core.files.uploadedfile import SimpleUploadedFile as UploadedFile 
    3333 
    3434__all__ = ( 
     
    4040    'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', 
    4141    'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 
    4242    'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField', 
    43     'TypedChoiceField' 
     43    'TypedChoiceField', 'DurationField', 
    4444) 
    4545 
    4646# These values, if given to to_python(), will trigger the self.required check. 
     
    884884            return datetime.datetime.combine(*data_list) 
    885885        return None 
    886886 
     887class DurationField(MultiValueField): 
     888    widget = DurationWidget 
     889 
     890    def __init__(self, *args, **kwargs): 
     891        errors = self.default_error_messages.copy() 
     892        fields = ( 
     893            IntegerField(max_value=999999999, min_value=-999999999), 
     894            IntegerField(label='Hours', max_value=23, min_value=0), 
     895            IntegerField(label='Minutes', max_value=59, min_value=0), 
     896            IntegerField(label='Seconds', max_value=59, min_value=0), 
     897            IntegerField(label='Microseconds', max_value=999999, min_value=0), 
     898        ) 
     899        super(DurationField, self).__init__(fields, *args, **kwargs) 
     900 
     901    def compress(self, data_list): 
     902        if data_list == [None] * 5: 
     903            raise ValidationError(gettext(u'This field is required.')) 
     904        if data_list: 
     905            return datetime.timedelta( 
     906                days=data_list[0] or 0, 
     907                hours=data_list[1] or 0, 
     908                minutes=data_list[2] or 0, 
     909                seconds=data_list[3] or 0, 
     910                microseconds=data_list[4] or 0, 
     911            ) 
     912        return None 
     913 
    887914ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') 
    888915 
    889916class IPAddressField(RegexField): 
  • django/forms/widgets.py

    old new  
    2626    'FileInput', 'DateInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput', 
    2727    'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 
    2828    'CheckboxSelectMultiple', 'MultiWidget', 
    29     'SplitDateTimeWidget', 
     29    'SplitDateTimeWidget', 'DurationWidget', 
    3030) 
    3131 
    3232MEDIA_TYPES = ('css','js') 
     
    706706    def __init__(self, attrs=None): 
    707707        widgets = (HiddenInput(attrs=attrs), HiddenInput(attrs=attrs)) 
    708708        super(SplitDateTimeWidget, self).__init__(widgets, attrs) 
     709 
     710class DurationWidget(MultiWidget): 
     711    def __init__(self, attrs=None): 
     712        attrs = attrs or {} 
     713        widgets = ( 
     714            TextInput(attrs=dict(attrs, size=4, maxlength=10, title='Days')), 
     715            TextInput(attrs=dict(attrs, size=1, maxlength=2, title='Hours')), 
     716            TextInput(attrs=dict(attrs, size=1, maxlength=2, title='Minutes')), 
     717            TextInput(attrs=dict(attrs, size=1, maxlength=2, title='Seconds')), 
     718            TextInput(attrs=dict(attrs, size=5, maxlength=6, title='Microseconds')), 
     719        ) 
     720        super(DurationWidget, self).__init__(widgets, attrs) 
     721 
     722    def decompress(self, value): 
     723        if value: 
     724            hours, seconds = divmod(value.seconds, 3600) 
     725            minutes, seconds = divmod(seconds, 60) 
     726            return [value.days, hours, minutes, seconds, value.microseconds] 
     727        return [None, None, None, None, None] 
     728 
     729    def format_output(self, rendered_widgets): 
     730        return u'%s days, %s : %s : %s . %s' % tuple(rendered_widgets)