Permalink
Browse files

Get previous and next published entries in only one query, because on…

…e goes with the other usually
  • Loading branch information...
1 parent 4001cec commit b42de7e020c9a27a58be0f78b0388351f7f1cea9 @Fantomas42 committed May 2, 2013
Showing with 95 additions and 22 deletions.
  1. +1 −1 docs/development/tests.rst
  2. +1 −2 docs/extensions/zinnia_docs.py
  3. +34 −11 zinnia/models_bases/entry.py
  4. +59 −8 zinnia/tests/entry.py
@@ -51,7 +51,7 @@ Coverage
Despite my best efforts, some functionnalities are not yet tested, that's why
I need your help !
-As I write these lines the **164** tests in Zinnia cover **96%** of the code
+As I write these lines the **165** tests in Zinnia cover **96%** of the code
bundled in Zinnia. A real effort has been made to obtain this percentage,
for ensuring the quality of the code.
@@ -12,8 +12,7 @@
def skip_model_member(app, what, name, obj, skip, options):
# These fields always fails !
- if name in ('tags', 'image',
- 'next_entry', 'previous_entry'):
+ if name in ('tags', 'image'):
return True
return skip
@@ -5,7 +5,6 @@
from django.utils.html import strip_tags
from django.utils.html import linebreaks
from django.contrib.sites.models import Site
-from django.utils.functional import cached_property
from django.contrib import comments
from django.contrib.comments.models import CommentFlag
from django.utils.translation import ugettext_lazy as _
@@ -97,25 +96,49 @@ def is_visible(self):
"""
return self.is_actual and self.status == PUBLISHED
- @cached_property
+ @property
def previous_entry(self):
"""
Returns the previous published entry if exists.
"""
- entries = self.__class__.published.filter(
- creation_date__lt=self.creation_date)[:1]
- if entries:
- return entries[0]
+ return self.previous_next_entries[0]
- @cached_property
+ @property
def next_entry(self):
"""
Returns the next published entry if exists.
"""
- entries = self.__class__.published.filter(
- creation_date__gt=self.creation_date).order_by('creation_date')[:1]
- if entries:
- return entries[0]
+ return self.previous_next_entries[1]
+
+ @property
+ def previous_next_entries(self):
+ """
+ Returns and caches a tuple containing the next
+ and previous published entries.
+ Only available if the entry instance is published.
+ """
+ previous_next = getattr(self, 'previous_next', None)
+
+ if previous_next is None:
+ if not self.is_visible:
+ previous_next = (None, None)
+ setattr(self, 'previous_next', previous_next)
+ return previous_next
+
+ entries = list(self.__class__.published.all())
+ index = entries.index(self)
+ try:
+ previous = entries[index + 1]
+ except IndexError:
+ previous = None
+
+ if index:
+ next = entries[index - 1]
+ else:
+ next = None
+ previous_next = (previous, next)
+ setattr(self, 'previous_next', previous_next)
+ return previous_next
@property
def short_url(self):
View
@@ -168,9 +168,17 @@ def test_short_url(self):
def test_previous_entry(self):
site = Site.objects.get_current()
+ with self.assertNumQueries(0):
+ # entry.previous_entry does not works until entry
+ # is published, so no query should be performed
+ self.assertFalse(self.entry.previous_entry)
+ self.entry.status = PUBLISHED
+ self.entry.save()
+ self.entry.sites.add(site)
+ del self.entry.previous_next # Invalidate the cached property
with self.assertNumQueries(1):
self.assertFalse(self.entry.previous_entry)
- with self.assertNumQueries(0):
+ # Reload to check the cache
self.assertFalse(self.entry.previous_entry)
params = {'title': 'My second entry',
'content': 'My second content',
@@ -179,10 +187,10 @@ def test_previous_entry(self):
'status': PUBLISHED}
self.second_entry = Entry.objects.create(**params)
self.second_entry.sites.add(site)
- del self.entry.previous_entry # Invalidate the cached_property
+ del self.entry.previous_next # Invalidate the cached property
with self.assertNumQueries(1):
self.assertEquals(self.entry.previous_entry, self.second_entry)
- with self.assertNumQueries(0):
+ # Reload to check the cache
self.assertEquals(self.entry.previous_entry, self.second_entry)
params = {'title': 'My third entry',
'content': 'My third content',
@@ -191,15 +199,24 @@ def test_previous_entry(self):
'status': PUBLISHED}
self.third_entry = Entry.objects.create(**params)
self.third_entry.sites.add(site)
- del self.entry.previous_entry
+ del self.entry.previous_next # Invalidate the cached property
self.assertEquals(self.entry.previous_entry, self.third_entry)
self.assertEquals(self.third_entry.previous_entry, self.second_entry)
+ self.assertFalse(self.second_entry.previous_entry)
def test_next_entry(self):
site = Site.objects.get_current()
+ with self.assertNumQueries(0):
+ # entry.next_entry does not works until entry
+ # is published, so no query should be performed
+ self.assertFalse(self.entry.previous_entry)
+ self.entry.status = PUBLISHED
+ self.entry.save()
+ self.entry.sites.add(site)
+ del self.entry.previous_next # Invalidate the cached property
with self.assertNumQueries(1):
self.assertFalse(self.entry.next_entry)
- with self.assertNumQueries(0):
+ # Reload to check the cache
self.assertFalse(self.entry.next_entry)
params = {'title': 'My second entry',
'content': 'My second content',
@@ -208,10 +225,10 @@ def test_next_entry(self):
'status': PUBLISHED}
self.second_entry = Entry.objects.create(**params)
self.second_entry.sites.add(site)
- del self.entry.next_entry # Invalidate the cached_property
+ del self.entry.previous_next # Invalidate the cached property
with self.assertNumQueries(1):
self.assertEquals(self.entry.next_entry, self.second_entry)
- with self.assertNumQueries(0):
+ # Reload to check the cache
self.assertEquals(self.entry.next_entry, self.second_entry)
params = {'title': 'My third entry',
'content': 'My third content',
@@ -220,9 +237,43 @@ def test_next_entry(self):
'status': PUBLISHED}
self.third_entry = Entry.objects.create(**params)
self.third_entry.sites.add(site)
- del self.entry.next_entry
+ del self.entry.previous_next # Invalidate the cached property
self.assertEquals(self.entry.next_entry, self.third_entry)
self.assertEquals(self.third_entry.next_entry, self.second_entry)
+ self.assertFalse(self.second_entry.next_entry)
+
+ def test_previous_next_entry_in_one_query(self):
+ site = Site.objects.get_current()
+ self.entry.status = PUBLISHED
+ self.entry.save()
+ self.entry.sites.add(site)
+ with self.assertNumQueries(1):
+ self.assertFalse(self.entry.previous_entry)
+ self.assertFalse(self.entry.next_entry)
+ # Reload to check the cache
+ self.assertFalse(self.entry.previous_entry)
+ self.assertFalse(self.entry.next_entry)
+ params = {'title': 'My second entry',
+ 'content': 'My second content',
+ 'slug': 'my-second-entry',
+ 'creation_date': datetime(2001, 1, 1),
+ 'status': PUBLISHED}
+ self.second_entry = Entry.objects.create(**params)
+ self.second_entry.sites.add(site)
+ params = {'title': 'My third entry',
+ 'content': 'My third content',
+ 'slug': 'my-third-entry',
+ 'creation_date': datetime(2050, 1, 1),
+ 'status': PUBLISHED}
+ self.third_entry = Entry.objects.create(**params)
+ self.third_entry.sites.add(site)
+ del self.entry.previous_next # Invalidate the cached property
+ with self.assertNumQueries(1):
+ self.assertEquals(self.entry.previous_entry, self.second_entry)
+ self.assertEquals(self.entry.next_entry, self.third_entry)
+ # Reload to check the cache
+ self.assertEquals(self.entry.previous_entry, self.second_entry)
+ self.assertEquals(self.entry.next_entry, self.third_entry)
def test_related_published(self):
site = Site.objects.get_current()

0 comments on commit b42de7e

Please sign in to comment.