slashmili / django-jalali

Jalali DateField support for Django model
http://pypi.python.org/pypi/django_jalali
BSD 3-Clause "New" or "Revised" License
254 stars 53 forks source link

Error in django admin on settings django's default timezone with pytz #55

Closed pooyamb closed 6 years ago

pooyamb commented 7 years ago

Hi, Why I try to set django's default timezone to non UTC timezones, ex. Asia/Tehran, I get an error in django admin changeform,

Assume this model :

from django.db import models
from django_jalali.db.models import jDateTimeField, jDateField
import jdatetime

class Book(models.Model):
    name = models.CharField(max_length=20)
    written_at = jDateField()
    collected_at = jDateTimeField(default=jdatetime.datetime.now)

And registering it with default modeladmin :

from django.contrib import admin
from .models import Book
from django_jalali import admin as j_admin

admin.site.register(Book)

when I set default timezone in settings.py

TIME_ZONE = 'Asia/Tehran'
USE_I18N = True
USE_L10N = True
USE_TZ = True

Error details: Trace back :

Environment:

Request Method: GET
Request URL: http://localhost:8000/admin/book/book/1/change/

Django Version: 1.11.3
Python Version: 3.5.2
Installed Applications:
('django_jalali',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'book')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware')

Template error:
In template /srv/venvs/jalali-test/lib/python3.5/site-packages/django/contrib/admin/templates/admin/includes/fieldset.html, error at line 19
   unsupported operand type for <: '<class 'datetime.datetime'>'   9 :             {% for field in line %}
   10 :                 <div{% if not line.fields|length_is:'1' %} class="field-box{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}"{% elif field.is_checkbox %} class="checkbox-row"{% endif %}>
   11 :                     {% if not line.fields|length_is:'1' and not field.is_readonly %}{{ field.errors }}{% endif %}
   12 :                     {% if field.is_checkbox %}
   13 :                         {{ field.field }}{{ field.label_tag }}
   14 :                     {% else %}
   15 :                         {{ field.label_tag }}
   16 :                         {% if field.is_readonly %}
   17 :                             <div class="readonly">{{ field.contents }}</div>
   18 :                         {% else %}
   19 :                              {{ field.field }} 
   20 :                         {% endif %}
   21 :                     {% endif %}
   22 :                     {% if field.field.help_text %}
   23 :                         <div class="help">{{ field.field.help_text|safe }}</div>
   24 :                     {% endif %}
   25 :                 </div>
   26 :             {% endfor %}
   27 :         </div>
   28 :     {% endfor %}
   29 : </fieldset>

Traceback:

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/core/handlers/base.py" in _legacy_get_response
  249.             response = self._get_response(request)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
  217.                 response = self.process_exception_by_middleware(e, request)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
  215.                 response = response.render()

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/response.py" in render
  107.             self.content = self.rendered_content

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/response.py" in rendered_content
  84.         content = template.render(context, self._request)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/backends/django.py" in render
  66.             return self.template.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render
  207.                     return self._render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in _render
  199.         return self.nodelist.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render
  990.                 bit = node.render_annotated(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/loader_tags.py" in render
  177.             return compiled_parent._render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in _render
  199.         return self.nodelist.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render
  990.                 bit = node.render_annotated(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/loader_tags.py" in render
  177.             return compiled_parent._render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in _render
  199.         return self.nodelist.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render
  990.                 bit = node.render_annotated(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/loader_tags.py" in render
  72.                 result = block.nodelist.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render
  990.                 bit = node.render_annotated(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/loader_tags.py" in render
  72.                 result = block.nodelist.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render
  990.                 bit = node.render_annotated(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/defaulttags.py" in render
  216.                     nodelist.append(node.render_annotated(context))

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/loader_tags.py" in render
  216.                 return template.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render
  209.                 return self._render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in _render
  199.         return self.nodelist.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render
  990.                 bit = node.render_annotated(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/defaulttags.py" in render
  216.                     nodelist.append(node.render_annotated(context))

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/defaulttags.py" in render
  216.                     nodelist.append(node.render_annotated(context))

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/defaulttags.py" in render
  322.                 return nodelist.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render
  990.                 bit = node.render_annotated(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/defaulttags.py" in render
  322.                 return nodelist.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render
  990.                 bit = node.render_annotated(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render
  1046.         return render_value_in_context(output, context)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/template/base.py" in render_value_in_context
  1024.     value = force_text(value)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/utils/encoding.py" in force_text
  76.                     s = six.text_type(s)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/utils/html.py" in <lambda>
  385.         klass.__str__ = lambda self: mark_safe(klass_str(self))

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/forms/boundfield.py" in __str__
  40.             return self.as_widget() + self.as_hidden(only_initial=True)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/forms/boundfield.py" in as_widget
  127.             **kwargs

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/forms/widgets.py" in render
  220.         context = self.get_context(name, value, attrs)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/forms/widgets.py" in get_context
  797.             value = self.decompress(value)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/forms/widgets.py" in decompress
  876.             value = to_current_timezone(value)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/forms/utils.py" in to_current_timezone
  192.         return timezone.make_naive(value, current_timezone)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/django/utils/timezone.py" in make_naive
  307.         value = timezone.normalize(value)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/pytz/tzinfo.py" in normalize
  242.         return self.fromutc(dt)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/pytz/tzinfo.py" in fromutc
  185.         idx = max(0, bisect_right(self._utc_transition_times, dt) - 1)

File "/srv/venvs/jalali-test/lib/python3.5/site-packages/jdatetime/__init__.py" in __lt__
  987.                 (type(other_datetime)))

Exception Type: TypeError at /admin/book/book/1/change/
Exception Value: unsupported operand type for <: '<class 'datetime.datetime'>'

After tracing the error(in django 1.11) I found out, django_jalali.admin.widgets.AdminSplitjDateTime inherits from django.forms.SplitDateTimeWidget,

in forms.SplitDateTimeWidget's decompress method, django tries to change field value to current timezone, to_current_timezone(forms.widgets:876)

in to_current_timezone method in forms.utils, if USE_TZ is set, it will try to timezone.make_naive the datetime value(making the datetime, timezone unaware) so it calls pytz methods, there it will try to compare jdatetime with native python's datetime and error occurs,

Posible solutions : The first solution I can think about is to override AdminSplitjDateTime's decompress method, but I don't know about backward incompatibility,

The second solution is to change jdatetime's behaviour in __lt__ and other comparing methods to accept native's datetime objects.

I should add that all this problems only happens when using pytz module, but as django 1.11, pytz is now a required dependency

I'm waiting for your response to work on it,

Django version : 1.11 with pytz (also tested on 1.10 1.9 1.8) Python version : 3.5.2

slashmili commented 7 years ago

@pooyamb I'm not doing much of python these days 😞

I would override AdminSplitjDateTime decompress method for now

Since jdatetime is a separate project I rather not to change it because of django-jalali

pooyamb commented 7 years ago

I forked this project and made some changes to fix compatibility issues with new django version, can you check it before I make a pull request? you're more familiar with it. here is the link

Hojjatrt commented 6 years ago

Hey guys, i have this problem with admin page (JDateFieldListFilter) too!! what is the solution? Django : 1.11 , Python : 3.5

slashmili commented 6 years ago

I fixed the timezone in master and drop support for Django 1.X.

If you need timezone please upgrade to Django 2.0