Extending Django CMS Page model - part II

Published at Dec. 14, 2010 | Tagged with: , , , ,

... or how to use the extended model in your templates and especially in the built-in navigation

The problem: As you can see in the previous post there is a very simple way to extend the page model with some custom fields without changing the core code. But how to use your custom fields inside your templates? If you have an instance of the Page model everything is fine. You can just access its properties and everything is fine. Unfortunately there is a problem if you try it inside a custom template used for show_menu template tag.

Speciality: I was really surprised when I tried to access my custom properties inside the mentioned above template and nothing happened. After some investigation I found that the elements in show_menu are not instances of the Page model but of NavigationNode. And even more surprised that NavigationNode does not contain instance of the Page model in its properties. So I was in need to extend the NavigationNode class.

Solution: First of all I decided to make it easier to access the custom properties I created before, so I added the following code to it:

class PageAvatars(models.Model):
    page = models.ForeignKey(Page, unique=True, verbose_name=_("Page"),
        editable=False, related_name='extended_fields')
    big_avatar      = FileBrowseField(_(u'Big Avatar'), max_length=255, blank=True)
    small_avatar    = FileBrowseField(_(u'Small Avatar'), max_length=255, blank=True)
def _big_avatar(obj):
    for e_f in obj.extended_fields.all():
      return e_f.big_avatar
    return None
Page._big_avatar = _big_avatar     
Page.big_avatar = property(lambda u: u._big_avatar())
For simplicity I have skipped the imports, but you can check them in the previous post So now I have a "shortcut" for accessing the big_avatar and it is time to make the Page object available in the NavigationNode.
from menus.base import NavigationNode
NavigationNode.page_instance = lambda u: Page.objects.filter(pk = u.id)[0]  
Line 2 defines a property("page_instance") that returns a Page object based on the primary key of the NavigationNode.id that equals to the corresponding Page primary key. Now you can use it in your custom navigation templates in the following way.
{% for child in children %}
{% endfor %}

Final words: I need to confess that I am really, really amazed how simply things can be done in Django and Django CMS. It is really awesome. I hope that you will find this post really helpful and useful. And as always do not be afraid to propose a better solutions or to ask for deeper explanation if something is not clear enough.