New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Django 1.9, 1.10, and 1.11 upgrades #125
Conversation
2 similar comments
caching/base.py
Outdated
https://github.com/django/django/commit/f3b7c059367a4e82bbfc7e4f0d42b10975e79f0c#diff-5b0dda5eb9a242c15879dc9cd2121379 | ||
""" | ||
if self._result_cache is None: | ||
self._result_cache = list(self.iterator()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this change will cause a server-side cursor to be used for _fetch_all
(if not globally disabled via DISABLE_SERVER_SIDE_CURSORS
) on Django 1.11.1+ due two these lines:
- https://github.com/vkurup/django-cache-machine/blob/6a547d8e4295985b2a84d4e115b1192cff540dbc/caching/base.py#L181
- https://github.com/django/django/blob/stable/1.11.x/django/db/models/query.py#L322-L323
I think we need to make sure _fetch_all()
still uses self._iterable_class()
directly rather than self.iterator()
on Django 1.11+, perhaps via something along the lines of the following:
- Separate our
iterator()
logic into a separate method, e.g.,_iterate_on(iterable)
- In
_fetch_all()
, calllist(self._iterate_on(iter(self._iterable_class(self))))
for Django 1.11+ orlist(self._iterate_on(self.iterator()))
for older Djangos (_iterable_class
unfortunately does not exist in Django 1.8). - In
iterator()
, simply returnself._iterate_on(super(CachingQuerySet, self).iterator())
Does that seem crazy? What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or better yet, rather than check the version directly, use duck typing as you already did here: 24c9779#diff-76150a51cd21d8d6b30aaaab22c7f114R179
_fetch_all()
and iterator()
are unchanged between Django 1.9-1.10, so it should be safe to use self._iterable_class
directly in those versions (in addition to Django 1.11)
_fetch_all()
:
- 1.9: https://github.com/django/django/blob/stable/1.9.x/django/db/models/query.py#L1072-L1076
- 1.10: https://github.com/django/django/blob/stable/1.10.x/django/db/models/query.py#L1085-L1089
iterator()
:
@tobiasmcnulty I made an update, trying to avoid overriding
Let me know what you think! |
3 similar comments
except StopIteration: | ||
if to_cache or config.CACHE_EMPTY_QUERYSETS: | ||
self.cache_objects(to_cache, query_key) | ||
raise |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change loses the StopIteration
re-raised here -- is that intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh... I think I missed that (I thought I was just simplifying an iteration through a iterator). I'll have to see if I can find a way to test that, or maybe just revert back to the old version of the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, perhaps it doesn't matter? https://stackoverflow.com/questions/4019971/how-to-implement-iter-self-for-a-container-object-python#comment67775147_4019987
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, this might be a part of Python that I just don't understand well. I'd think that I could just fix this by adding a raise StopIteration
on line 128 (at the same indentation level as the if
on line 126). But that seems superfluous, since it's the last line of the method and the iteration will end anyways. Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
caching/base.py
Outdated
def __iter__(self): | ||
if hasattr(super(CacheInternalCommonMixin, self), '__iter__'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps try:
/except AttributeError:
to avoid hasattr()
usage? The Django 1.8 bit can go away eventually, and there will be no performance hit for Django 1.9+
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
caching/base.py
Outdated
cache.add(query_key, objects, timeout=self.timeout) | ||
invalidator.cache_objects(self.model, objects, query_key, query_flush) | ||
|
||
class CacheMachine(CacheInternalCommonMixin): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not entirely sure this will work, but I wonder if CacheInternalCommonMixin
, CacheMachine
, and CachingModelIterable
could all be collapsed into a single class (e.g., by setting ModelIterable = object
when it fails to import). Definitely ignore this comment if that would complicate rather than simplify things!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried doing that, but gave up early because of issues that I can't remember. But some of those problems might have been 2 small issues in the tests which I since identified, so let me try again.
caching/base.py
Outdated
yield obj | ||
return | ||
|
||
# Try to fetch from the cache. | ||
try: | ||
query_key = self.query_key() | ||
except query.EmptyResultSet: | ||
raise StopIteration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given our findings about StopIteration
elsewhere, I think this should be converted to a simple return
caching/base.py
Outdated
@@ -309,11 +332,13 @@ def __iter__(self): | |||
while True: | |||
yield next(iterator) | |||
else: | |||
sql = self.raw_query % tuple(self.params) | |||
for obj in CacheMachine(self.model, sql, iterator, timeout=self.timeout): | |||
for obj in CacheMachine(self, iterator, timeout=self.timeout): | |||
yield obj | |||
raise StopIteration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this line can be deleted (so we get the implicit return None
instead)
try to simplify Django 1.8/Django 1.11 compatibility
⛵️ Thanks again for taking this on! |
Starting with #119, aim to get this working on Django 1.11. It currently only works in 1.8