Login

Menu/navigation bars in a tag

Author:
ep
Posted:
July 29, 2007
Language:
Python
Version:
.96
Score:
4 (after 4 ratings)

This tag makes it easy to include menu or navigation bars in an application's pages, even in a 'tabbed' fashion. (The actual appearance is styled in CSS; this example uses UL tags that are then supposed to be styled by a "display: inline" attribute to be rendered as horizontal bars.)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
from django import template
from django.conf import settings
from django.utils.translation import ugettext as _

class SimpleMenu(object):
	"""
	Stores a tree-like menu hierarchy, and renders it up to a chosen item
	on request.
	"""
	def __init__(self, menulist):
		"""
		Initializes the class.
		
		The menulist argument is a dictionary of menus. Each key is the menu
		label; the corresponding value is a list of (item_label, urlname)
		pairs.  The 'urlname' is then looked up in the URL configuration in
		order to correctly render the matching link. (See example below.)
		"""
		self.menus = menulist

	def render(self, menu_name, depth=0, active=None):
		"""
		The render() method returns a HTML string suitable for use as a
		menu bar on a given page.
		
		menu_name: the label of a menu, as specified at class initialization.
		
		depth (kw): an integer specifying how far into the tree this is being
		rendered.  Usually the render() method takes care of the whole menu,
		but it may be occasionally useful to delegate a single sub-menu to the
		SimpleMenu class.  Note that this parameter only affects the CSS class.
		
		active(kw): the active label, if any, in the menu.
		"""
		from django.core.urlresolvers import reverse
		s = '<div class="menu depth%d"><ul>' % depth
		for label, view in self.menus[menu_name]:
			s += '<li class="%s"><a href="%s">%s</a></li>' % (['nonactive',
				'active'][view==active], reverse(view), label)
		return s + '</ul></div>'

menu = SimpleMenu(settings.MENUITEMS)

# Example:
# MENUITEMS = {
#	'root': ((_('Venues'), 'venue_menu'), (_('My account'), 'account_menu')),
#	'venue_menu': ((_('Browse'), 'venue_browse'), (_('New venue'), 'eatout-add-venue'),)
#}



class MenuNode(template.Node):
	"""
	The menu tag takes a menu path whose components are labels separated by spaces.
	All the components from the first to the next-to-last are menu labels, and
	they are going to be rendered as menu bars.  Since they are seen as
	sub-items, or (if you will) as nested tabs in a web page, each component
	is also the active component in the previous menu.
	
	The last component is not rendered as a menu, but it is taken to be the
	active item in the last menu (that is, the next-to-last component).
	"""
	def __init__(self, menu_path):
		self.menu_path = menu_path

	def render(self, context):
		# Item = (item, follower)
		for i in range(len(self.menu_path)):
			# Strip any single quotes from the string edges
			self.menu_path = [s.rstrip('"\'').lstrip('"\'')
				for s in self.menu_path]
		self.context = context
		# Render each couple: (1,2), (2,3), (3,4), ...
		return ''.join([self._render_menu(self.menu_path[index],	
			active=self.menu_path[index+1], depth=index)
			for index in range(len(self.menu_path)-1)])

	def _render_menu(self, menu_name, active=None, depth=None):
		"""
		If the menu has its own template, then use the template.  Otherwise,
		ask its class to do the rendering.
		"""
		from django.template.loader import get_template
		from django.template import TemplateDoesNotExist
		try:
			menu_template = get_template('menu/%s.html' % menu_name)
			self.context['active'] = active
			return menu_template.render(self.context)
		except TemplateDoesNotExist:
			return menu.render(menu_name, depth=depth, active=active)

def do_menu(parser, token):
	menu_path = token.split_contents()
	return MenuNode(menu_path[1:])

# Usage example:
# {% menu root venue_menu new_visit %}
# will render the 'root' menu with the 'venue_menu' item, if it exists, as
# active; then the 'venue_menu_ menu with the 'new_visit' item, if it
# exists, as active.

register.tag('menu', do_menu)

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 2 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 2 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 9 months, 2 weeks ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 10 months, 1 week ago
  5. Help text hyperlinks by sa2812 11 months ago

Comments

sebastianmacias (on August 2, 2007):

MENUITEMS = { 'root': ((('Venues'), 'venue_menu'), (('My account'), 'account_menu')), 'venue_menu': ((('Browse'), 'venue_browse'), (('New venue'), 'eatout-add-venue'),) }

seems to be broken. When I add that to my settings file I get the following error:

File "manage.py", line 4, in <module> import settings # Assumed to be in the same directory. File "/home/sebastian/django_root/championsound/settings.py", line 144, in <module> 'root': ((('Venues'), 'venue_menu'), (('My account'), 'account_menu')), File "/usr/lib/python2.5/site-packages/django/utils/translation/init.py", line 61, in ugettext return real_ugettext(message) File "/usr/lib/python2.5/site-packages/django/utils/translation/init.py", line 31, in delayed_loader if settings.USE_I18N: File "/usr/lib/python2.5/site-packages/django/conf/init.py", line 28, in getattr self._import_settings() File "/usr/lib/python2.5/site-packages/django/conf/init.py", line 55, in _import_settings raise EnvironmentError, "Environment variable %s is undefined." % ENVIRONMENT_VARIABLE EnvironmentError: Environment variable DJANGO_SETTINGS_MODULE is undefined.

I am importing ugettext as _

#

Please login first before commenting.