Django

Code

Ticket #2443: durationfield.5.diff

File durationfield.5.diff, 9.0 kB (added by Gulopine, 2 years ago)

Updated to work with latest trunk, and to no longer rely on #3982

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

    old new  
    686686        defaults.update(kwargs) 
    687687        return super(DecimalField, self).formfield(**defaults) 
    688688 
     689class DurationProxy(object): 
     690    def __init__(self, field): 
     691        self.field_name = field.name 
     692 
     693    def __get__(self, instance=None, owner=None): 
     694        if instance is None: 
     695            raise AttributeError, "%s can only be accessed from %s instances." % (self.field_name, owner.__name__) 
     696        if self.field_name not in instance.__dict__: 
     697            return None 
     698        return instance.__dict__[self.field_name] 
     699 
     700    def __set__(self, instance, value): 
     701        if not isinstance(value, datetime.timedelta): 
     702            value = datetime.timedelta(seconds=float(value)) 
     703        instance.__dict__[self.field_name] = value 
     704 
     705class DurationField(Field): 
     706    def __init__(self, *args, **kwargs): 
     707        super(DurationField, self).__init__(*args, **kwargs) 
     708        self.max_digits, self.decimal_places = 20, 6 
     709 
     710    def get_internal_type(self): 
     711        return "DecimalField" 
     712 
     713    def contribute_to_class(self, cls, name): 
     714        super(DurationField, self).contribute_to_class(cls, name) 
     715        setattr(cls, name, DurationProxy(self)) 
     716 
     717    def get_db_prep_save(self, value): 
     718        print 'DurationField.get_db_prep_save' 
     719        if value is None: 
     720            return None 
     721        return str(value.days * 24 * 3600 + value.seconds + float(value.microseconds) / 1000000) 
     722 
     723    def to_python(self, value): 
     724        if isinstance(value, datetime.timedelta): 
     725            return value 
     726        try: 
     727            return datetime.timedelta(seconds=float(value)) 
     728        except (TypeError, ValueError): 
     729            raise validators.ValidationError('This value must be a real number.') 
     730        except OverflowError: 
     731            raise validators.ValidationError('The maximum allowed value is %s' % datetime.timedelta.max) 
     732 
     733    def flatten_data(self, follow, obj=None): 
     734        print 'DurationField.flatten_data' 
     735        val = self._get_val_from_obj(obj) 
     736        if val is None or val is '': 
     737            return '' 
     738        return {self.name: self.get_db_prep_save(val)} 
     739 
     740    def formfield(self, form_class=forms.DurationField, **kwargs): 
     741        return super(DurationField, self).formfield(form_class, **kwargs) 
     742 
     743    def get_manipulator_field_objs(self): 
     744        return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)] 
     745 
    689746class EmailField(CharField): 
    690747    def __init__(self, *args, **kwargs): 
    691748        kwargs['max_length'] = kwargs.get('max_length', 75) 
  • django/newforms/fields.py

    old new  
    2020from django.utils.encoding import StrAndUnicode, smart_unicode 
    2121 
    2222from util import ErrorList, ValidationError 
    23 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput 
     23from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput, DurationWidget 
    2424 
    2525 
    2626__all__ = ( 
     
    3131    'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 
    3232    'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', 
    3333    'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 
    34     'SplitDateTimeField', 'IPAddressField', 
     34    'SplitDateTimeField', 'IPAddressField', 'DurationField', 
    3535) 
    3636 
    3737# These values, if given to to_python(), will trigger the self.required check. 
     
    753753 
    754754    def __init__(self, *args, **kwargs): 
    755755        super(IPAddressField, self).__init__(ipv4_re, *args, **kwargs) 
     756 
     757class DurationField(MultiValueField): 
     758    widget = DurationWidget 
     759 
     760    def __init__(self, *args, **kwargs): 
     761        errors = self.default_error_messages.copy() 
     762        fields = ( 
     763            IntegerField(max_value=999999999, min_value=-999999999), 
     764            IntegerField(label='Hours', max_value=23, min_value=0), 
     765            IntegerField(label='Minutes', max_value=59, min_value=0), 
     766            IntegerField(label='Seconds', max_value=59, min_value=0), 
     767            IntegerField(label='Microseconds', max_value=999999, min_value=0), 
     768        ) 
     769        super(DurationField, self).__init__(fields, *args, **kwargs) 
     770 
     771    def compress(self, data_list): 
     772        if data_list == [None] * 5: 
     773            raise ValidationError(gettext(u'This field is required.')) 
     774        if data_list: 
     775            return datetime.timedelta( 
     776                days=data_list[0] or 0, 
     777                hours=data_list[1] or 0, 
     778                minutes=data_list[2] or 0, 
     779                seconds=data_list[3] or 0, 
     780                microseconds=data_list[4] or 0, 
     781            ) 
     782        return None 
  • django/newforms/widgets.py

    old new  
    2222    'FileInput', 'DateTimeInput', 'Textarea', 'CheckboxInput', 
    2323    'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 
    2424    'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget', 
     25    'DurationWidget', 
    2526) 
    2627 
    2728class Widget(object): 
     
    450451        if value: 
    451452            return [value.date(), value.time().replace(microsecond=0)] 
    452453        return [None, None] 
     454 
     455class DurationWidget(MultiWidget): 
     456    def __init__(self, attrs=None): 
     457        attrs = attrs or {} 
     458        widgets = ( 
     459            TextInput(attrs=dict(attrs, size=4, maxlength=10, title='Days')), 
     460            TextInput(attrs=dict(attrs, size=1, maxlength=2, title='Hours')), 
     461            TextInput(attrs=dict(attrs, size=1, maxlength=2, title='Minutes')), 
     462            TextInput(attrs=dict(attrs, size=1, maxlength=2, title='Seconds')), 
     463            TextInput(attrs=dict(attrs, size=5, maxlength=6, title='Microseconds')), 
     464        ) 
     465        super(DurationWidget, self).__init__(widgets, attrs) 
     466 
     467    def decompress(self, value): 
     468        if value: 
     469            hours, seconds = divmod(value.seconds, 3600) 
     470            minutes, seconds = divmod(seconds, 60) 
     471            return [value.days, hours, minutes, seconds, value.microseconds] 
     472        return [None, None, None, None, None] 
     473 
     474    def format_output(self, rendered_widgets): 
     475        return u'%s days, %s : %s : %s . %s' % tuple(rendered_widgets) 
  • docs/model-api.txt

    old new  
    218218 
    219219The admin represents this as an ``<input type="text">`` (a single-line input). 
    220220 
     221``DurationField`` 
     222~~~~~~~~~~~~~~~~~ 
     223 
     224**New in Django development version** 
     225 
     226A span of time, represented in Python by a ``timedelta`` instance. 
     227 
     228The admin represents this as an ``<input type="text">`` (a single-line input), 
     229with its value representing the number of seconds in the duration. Fractional 
     230values are allowed, with a millisecond precision. 
     231 
    221232``EmailField`` 
    222233~~~~~~~~~~~~~~ 
    223234 
  • docs/newforms.txt

    old new  
    12751275permitted in the value, whilst ``decimal_places`` is the maximum number of 
    12761276decimal places permitted. 
    12771277 
     1278``DurationField`` 
     1279~~~~~~~~~~~~~~~~~ 
     1280 
     1281**New in Django development version** 
     1282 
     1283    * Default widget: ``DurationWidget`` 
     1284    * Empty value: ``None`` 
     1285    * Normalizes to: A Python ``datetime.timedelta`` object 
     1286    * Validates that the given value is a length of time, such as 24 hours in a 
     1287      day, 60 minutes in an hour, and 60 seconds in a minute. 
     1288 
     1289A span of time, represented in Python by a ``timedelta`` instance. 
     1290 
     1291The admin represents this as an ``<input type="text">`` (a single-line input), 
     1292with its value representing the number of seconds in the duration. Fractional 
     1293values are allowed, with a millisecond precision. 
     1294 
    12781295``EmailField`` 
    12791296~~~~~~~~~~~~~~ 
    12801297 
     
    16181635    ``MultiWidget``               Wrapper around multiple other widgets 
    16191636    ``SplitDateTimeWidget``       Wrapper around two ``TextInput`` widgets: 
    16201637                                  one for the Date, and one for the Time. 
     1638    ``DurationWidget``            A set of 5 ``TextInput`` widgets arranged 
     1639                                  as ``[   ] days, [ ]:[ ]:[ ].[    ]`` 
    16211640    ============================  =========================================== 
    16221641 
    1623 **New in Django development version:** The ``DateTimeInput`` has been adde
    1624 since the last release. 
     1642**New in Django development version:** The ``DateTimeInput`` an
     1643``DurationWidget`` have been added since the last release. 
    16251644 
    16261645Specifying widgets 
    16271646------------------