#30758: Postgres DateTimeRangeField crash in django admin (AttributeError:
'builtin_function_or_method' object has no attribute 'strip')
------------------------------------+--------------------------------------
     Reporter:  yeppus              |                    Owner:  (none)
         Type:  Bug                 |                   Status:  new
    Component:  contrib.postgres    |                  Version:  2.2
     Severity:  Normal              |               Resolution:
     Keywords:  DateTimeRangeField  |             Triage Stage:  Unreviewed
    Has patch:  0                   |      Needs documentation:  0
  Needs tests:  0                   |  Patch needs improvement:  0
Easy pickings:  0                   |                    UI/UX:  0
------------------------------------+--------------------------------------
Description changed by felixxm:

Old description:

> When trying to save an object that has a DateTimeRangeField in django
> admin the following error occurs:
> AttributeError: 'builtin_function_or_method' object has no attribute
> 'strip'
>
> When trying to determine if the value has changed it, maybe accidentally,
> assigns initial value the function "lower" instead of the value.
>
> Later it tries to run .strip() on the function.
>
> This all worked in django 1.11 and I can not find any mentioned of
> changed behaviour for Django 2.2.
>

> **django/contrib/postgres/forms/ranges.py: 101**
> {{{
> #!div style="font-size: 80%"
> Code highlighting:
>   {{{#!python
>   class RangeWidget(MultiWidget):
>     def __init__(self, base_widget, attrs=None):
>         widgets = (base_widget, base_widget)
>         super().__init__(widgets, attrs)
>
>     def decompress(self, value):
>         if value:
>             return (value.lower, value.upper) ### <<-- RETURNS CALLABLE,
> NOT VALUE
>         return (None, None)
>   }}}
> }}}
>
> **django/forms/fields.py: 1060**
> {{{
> #!div style="font-size: 80%"
> Code highlighting:
>   {{{#!python
>     def has_changed(self, initial, data):
>         if self.disabled:
>             return False
>         if initial is None:
>             initial = ['' for x in range(0, len(data))]
>         else:
>             if not isinstance(initial, list):
>                 initial = self.widget.decompress(initial) ### <<--
> RECEIVES CALLABLE, NOT VALUE
>         for field, initial, data in zip(self.fields, initial, data):
>             try:
>                 initial = field.to_python(initial) ### <<-- TRIES
> to_python with CALLABLE
>             except ValidationError:
>                 return True
>             if field.has_changed(initial, data):
>                 return True
>         return False
>   }}}
> }}}
>
> **django/forms/fields.py: 450**
> {{{
> #!div style="font-size: 80%"
> Code highlighting:
>   {{{#!python
>     def to_python(self, value):
>         """
>         Validate that the input can be converted to a datetime. Return a
>         Python datetime.datetime object.
>         """
>         if value in self.empty_values:
>             return None
>         if isinstance(value, datetime.datetime):
>             return from_current_timezone(value)
>         if isinstance(value, datetime.date):
>             result = datetime.datetime(value.year, value.month,
> value.day)
>             return from_current_timezone(result)
>         result = super().to_python(value) ### <<-- ENDS UP HERE SENDING
> CALLABLE TO PARENT
>         return from_current_timezone(result)
>   }}}
> }}}
>
> BaseTemporalField.to_python expects a string and runs .strip() which
> generates AttributeError and crashes.
>

> Traceback (most recent call last):
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/core/handlers/exception.py", line 34, in inner
>     response = get_response(request)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/core/handlers/base.py", line 115, in _get_response
>     response = self.process_exception_by_middleware(e, request)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/core/handlers/base.py", line 113, in _get_response
>     response = wrapped_callback(request, *callback_args,
> **callback_kwargs)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/contrib/admin/options.py", line 606, in wrapper
>     return self.admin_site.admin_view(view)(*args, **kwargs)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/utils/decorators.py", line 142, in _wrapped_view
>     response = view_func(request, *args, **kwargs)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/views/decorators/cache.py", line 44, in
> _wrapped_view_func
>     response = view_func(request, *args, **kwargs)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/contrib/admin/sites.py", line 223, in inner
>     return view(request, *args, **kwargs)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/contrib/admin/options.py", line 1637, in change_view
>     return self.changeform_view(request, object_id, form_url,
> extra_context)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/utils/decorators.py", line 45, in _wrapper
>     return bound_method(*args, **kwargs)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/utils/decorators.py", line 142, in _wrapped_view
>     response = view_func(request, *args, **kwargs)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/contrib/admin/options.py", line 1522, in changeform_view
>     return self._changeform_view(request, object_id, form_url,
> extra_context)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/contrib/admin/options.py", line 1560, in _changeform_view
>     if all_valid(formsets) and form_validated:
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/forms/formsets.py", line 448, in all_valid
>     valid &= formset.is_valid()
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/forms/formsets.py", line 301, in is_valid
>     self.errors
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/forms/formsets.py", line 281, in errors
>     self.full_clean()
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/forms/formsets.py", line 325, in full_clean
>     if not form.has_changed() and i >= self.initial_form_count():
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/contrib/admin/options.py", line 2111, in has_changed
>     return super().has_changed()
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/forms/forms.py", line 434, in has_changed
>     return bool(self.changed_data)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/utils/functional.py", line 80, in __get__
>     res = instance.__dict__[self.name] = self.func(instance)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/forms/forms.py", line 456, in changed_data
>     if field.has_changed(initial_value, data_value):
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/forms/fields.py", line 1070, in has_changed
>     initial = field.to_python(initial)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/forms/fields.py", line 462, in to_python
>     result = super().to_python(value)
>   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
> packages/django/forms/fields.py", line 379, in to_python
>     value = value.strip()

New description:

 When trying to save an object that has a DateTimeRangeField in django
 admin the following error occurs:
 AttributeError: 'builtin_function_or_method' object has no attribute
 'strip'

 When trying to determine if the value has changed it, maybe accidentally,
 assigns initial value the function "lower" instead of the value.

 Later it tries to run .strip() on the function.

 This all worked in django 1.11 and I can not find any mentioned of changed
 behaviour for Django 2.2.


 **django/contrib/postgres/forms/ranges.py: 101**
 {{{
 #!div style="font-size: 80%"
 Code highlighting:
   {{{#!python
   class RangeWidget(MultiWidget):
     def __init__(self, base_widget, attrs=None):
         widgets = (base_widget, base_widget)
         super().__init__(widgets, attrs)

     def decompress(self, value):
         if value:
             return (value.lower, value.upper) ### <<-- RETURNS CALLABLE,
 NOT VALUE
         return (None, None)
   }}}
 }}}

 **django/forms/fields.py: 1060**
 {{{
 #!div style="font-size: 80%"
 Code highlighting:
   {{{#!python
     def has_changed(self, initial, data):
         if self.disabled:
             return False
         if initial is None:
             initial = ['' for x in range(0, len(data))]
         else:
             if not isinstance(initial, list):
                 initial = self.widget.decompress(initial) ### <<--
 RECEIVES CALLABLE, NOT VALUE
         for field, initial, data in zip(self.fields, initial, data):
             try:
                 initial = field.to_python(initial) ### <<-- TRIES
 to_python with CALLABLE
             except ValidationError:
                 return True
             if field.has_changed(initial, data):
                 return True
         return False
   }}}
 }}}

 **django/forms/fields.py: 450**
 {{{
 #!div style="font-size: 80%"
 Code highlighting:
   {{{#!python
     def to_python(self, value):
         """
         Validate that the input can be converted to a datetime. Return a
         Python datetime.datetime object.
         """
         if value in self.empty_values:
             return None
         if isinstance(value, datetime.datetime):
             return from_current_timezone(value)
         if isinstance(value, datetime.date):
             result = datetime.datetime(value.year, value.month, value.day)
             return from_current_timezone(result)
         result = super().to_python(value) ### <<-- ENDS UP HERE SENDING
 CALLABLE TO PARENT
         return from_current_timezone(result)
   }}}
 }}}

 BaseTemporalField.to_python expects a string and runs .strip() which
 generates AttributeError and crashes.

 {{{
 Traceback (most recent call last):
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/core/handlers/exception.py", line 34, in inner
     response = get_response(request)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/core/handlers/base.py", line 115, in _get_response
     response = self.process_exception_by_middleware(e, request)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/core/handlers/base.py", line 113, in _get_response
     response = wrapped_callback(request, *callback_args,
 **callback_kwargs)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/contrib/admin/options.py", line 606, in wrapper
     return self.admin_site.admin_view(view)(*args, **kwargs)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/utils/decorators.py", line 142, in _wrapped_view
     response = view_func(request, *args, **kwargs)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
     response = view_func(request, *args, **kwargs)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/contrib/admin/sites.py", line 223, in inner
     return view(request, *args, **kwargs)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/contrib/admin/options.py", line 1637, in change_view
     return self.changeform_view(request, object_id, form_url,
 extra_context)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/utils/decorators.py", line 45, in _wrapper
     return bound_method(*args, **kwargs)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/utils/decorators.py", line 142, in _wrapped_view
     response = view_func(request, *args, **kwargs)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/contrib/admin/options.py", line 1522, in changeform_view
     return self._changeform_view(request, object_id, form_url,
 extra_context)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/contrib/admin/options.py", line 1560, in _changeform_view
     if all_valid(formsets) and form_validated:
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/forms/formsets.py", line 448, in all_valid
     valid &= formset.is_valid()
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/forms/formsets.py", line 301, in is_valid
     self.errors
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/forms/formsets.py", line 281, in errors
     self.full_clean()
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/forms/formsets.py", line 325, in full_clean
     if not form.has_changed() and i >= self.initial_form_count():
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/contrib/admin/options.py", line 2111, in has_changed
     return super().has_changed()
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/forms/forms.py", line 434, in has_changed
     return bool(self.changed_data)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/utils/functional.py", line 80, in __get__
     res = instance.__dict__[self.name] = self.func(instance)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/forms/forms.py", line 456, in changed_data
     if field.has_changed(initial_value, data_value):
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/forms/fields.py", line 1070, in has_changed
     initial = field.to_python(initial)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/forms/fields.py", line 462, in to_python
     result = super().to_python(value)
   File "/Users/joakim/.pyenv/versions/3.5.2/lib/python3.5/site-
 packages/django/forms/fields.py", line 379, in to_python
     value = value.strip()
 }}}

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30758#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/064.9aa447b79c8587913ebc67635a1933ed%40djangoproject.com.

Reply via email to