There is a neat piece of functionality in Django that will allow you to traverse your object graph by date:
Get next or previous by FOO, where FOO is a Date or DateTime field:
For every DateField and DateTimeField that does not have null=True, the object will have get_next_by_FOO() and get_previous_by_FOO() methods, where FOO is the name of the field. This returns the next and previous object with respect to the date field, raising the appropriate DoesNotExist exception when appropriate...
However, these methods use the default manager, that may be not what one wants.
Don't override
You could override these methods, but admin and many other contrib applications rely on them to return every single object, so you'd break them. Maybe this should be fixed in the trunk by specifying two default managers: one named all and another one named objects. The first could be used by admin-like applications, like admin or databrowse, and the other for applications that display stuff to users.
Anyway, until this is done, we can't override, so instead we'll add new methods.
DIY
Fortunately, we can reuse the functionality of these nice methods, since they accept filter arguments. But, in the principle of DRY, duplicating the filter arguments in many places isn't nice. What I prefer doing is, in each manager that overrides get_query_set, to define a filter_dict property:
class PublicEntriesManager(models.Manager):
def __init__(self, *args, **kwargs):
self.filter_dict = dict(is_public=True, pub_date__lte = datetime.now)
super(PublicEntriesManager, self).__init__(*args, **kwargs)
def get_query_set(self):
return super(PublicEntriesManager, self).get_query_set().filter(**self.filter_dict)
...which, I reuse at the get_query_set. I can now use that everywhere that I need to provide the same functionality, without using the manager. In our case, in get_next_entry:
def get_next_entry(self):
return self.get_next_by_pub_date(**Entry.public.filter_dict)
def get_previous_entry(self):
return self.get_previous_by_pub_date(**Entry.public.filter_dict)
You can of course reference other properties as well:
def get_next_in_lang(self):
return self.get_next_by_pub_date(language=self.language, **Entry.public.filter_dict )
def get_previous_in_lang(self):
return self.get_previous_by_pub_date(language=self.language, **Entry.public.filter_dict )
Templates
{% if entry.get_previous_in_lang %}
<span>
{% trans 'Previous entry:' %}
<a href="{{entry.get_previous_in_lang.get_absolute_url}}"
title="{{entry.get_previous_in_lang.title}}">{{entry.get_previous_in_lang.title}}</a>
</span>
<br />
{% endif %}
Conclusion
What I like about Django is that it forces me to add methods to my models so I can use them in the templates. If I could pass arguments to a method call in the template, I'd be tempted to hard-code the filter arguments in the template, forcing me to hunt around for references if I wanted to change something.
Adding smarts to the model class is the way to go, IMHO.
This post is older than 30 days and comments have been turned off.
