jbalogh/django-cache-machine — pull request #85 by emorozov (2014-12-10 15:15:07+00:00)
Title: Fix TypeError caused by incorrect CachedQuerySet unpickling.

This problem is present in both PyPI version and latest github version of django-cache-machine. It manifests itself like this:
Traceback:
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/core/handlers/base.py" in get_response
112. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/views/generic/base.py" in view
69. return self.dispatch(request, *args, **kwargs)
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/views/decorators/csrf.py" in wrapped_view
57. return view_func(*args, **kwargs)
File "/data/cache/buildout/eggs/djangorestframework-2.3.6-py2.7.egg/rest_framework/views.py" in dispatch
327. response = self.handle_exception(exc)
File "/data/cache/buildout/eggs/djangorestframework-2.3.6-py2.7.egg/rest_framework/views.py" in dispatch
324. response = handler(request, *args, **kwargs)
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/utils/decorators.py" in _wrapper
29. return bound_func(*args, **kwargs)
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/utils/decorators.py" in _wrapped_view
99. response = view_func(request, *args, **kwargs)
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/utils/decorators.py" in bound_func
25. return func(self, *args2, **kwargs2)
File "/home/jmv/linguist/staging/src/lingq_api/views.py" in wrapper
86. language = get_object_or_404(profile.languages(), code=language)
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/shortcuts/__init__.py" in get_object_or_404
113. return queryset.get(*args, **kwargs)
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/db/models/query.py" in get
304. num = len(clone)
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/db/models/query.py" in __len__
77. self._fetch_all()
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/db/models/query.py" in _fetch_all
857. self._result_cache = list(self.iterator())
File "/home/jmv/linguist/staging/parts/django-cache-machine/caching/base.py" in __iter__
122. self.cache_objects(to_cache)
File "/home/jmv/linguist/staging/parts/django-cache-machine/caching/base.py" in cache_objects
129. cache.add(query_key, objects, timeout=self.timeout)
File "/data/cache/buildout/eggs/django_devserver-0.8.0-py2.7.egg/devserver/utils/stats.py" in wrapped
99. return stats.run(func, key, logger, *args, **kwargs)
File "/data/cache/buildout/eggs/django_devserver-0.8.0-py2.7.egg/devserver/utils/stats.py" in run
23. value = func(*args, **kwargs)
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/core/cache/backends/memcached.py" in add
71. return self._cache.add(key, value, self._get_memcache_timeout(timeout))
File "/data/cache/buildout/eggs/Django-1.6.8-py2.7.egg/django/core/cache/backends/memcached.py" in _get_memcache_timeout
50. elif int(timeout) == 0:

Exception Type: TypeError at /api/languages/es/progress/
Exception Value: int() argument must be a string or a number, not 'object'

It was very difficult to find the culprit, but finally I've found out that CachedQuerySet stores Django DEFAULT_TIMEOUT in ints instances, which is a marker object. Django caching code compares this value with DEFAULT_TIMEOUT and if they are not identical, assumes that timeout is an integer value. But in unpickled instances, timeout value is always some different object.

My fix is not the most elegant, but I've spent 5+ hours trying to debug this issue and it makes any development using d-c-m impossible (bug appears and disappears at random).