李迎辉 2009年08月11日 星期二 11:03 | 1433次浏览 | 0条评论
学了几天的drupal,感受最大的就是许多东西都是通过hook可以扩展的。这一点与ulipad或uliweb的设计很接近。在uliweb设计之初 就提供了与ulipad类似的mixin机制。只不过,机制是有,但是如何使用它,如何用好它是需要进一步设计的。在drupal中,一个新设计的模块是 可以向系统中添加菜单的。于是我尝试在admin中添加一个类似的扩展机制。因为在admin中,界面显示为多tab的方式,可以看到是菜单,只不过,目 前只是一层,不是二层的。
先想一下菜单应该有哪些信息,目前我设计是:order, id, caption, endpoint, parent_id。
其中order相当于drupal中常见的weight,是用来排序的。
id是菜单的唯一标识,如果没有设置则与caption一致。
caption是菜单提示。
endpoint是菜单对应的view函数字符串,它可以通过使用url_for来获得实际的url。
parent_id是父菜单的id,是为了以后支持多级菜单进行的扩展。
在admin中,一个菜单将显示为
<ul>
<li>...</li>
<li>...</li>
</ul>
这样的东西,通过css属性展示为tab形式的菜单。
在admin_layout.html中有一行 {{from uliweb.contrib.admin.menu import menu}}的代码,它会导入一个menu函数到模板中。
然后在相应的子模块中会这样调用:
{{<< menu('Settings') }}
menu函数会真正显示菜单。它的主要代码如下:
def menu(current='settings'):
out = StringIO()
menus = []
call(None, 'add_menu', menus)
mlist = mergemenu(menus)
out.write('<ul>')
for m in mlist[None]:
weight, id, caption, endpoint = m
if id == current:
out.write('<li id="current"><strong>%s</strong></li>' % caption)
else:
out.write('<li><a href="%s">%s</a></li>' % (url_for(endpoint), caption))
submenu(mlist, id)
out.write('</ul>')
return out.getvalue()
它会首先调用dispatch中的call,它是一个类似于drupal的hook调用,用于搜索所有相关的菜单信息。信息的格式为:
(parent_id, (order, id, caption, endpoint))
一级菜单的parent_id应该是None.
然后通过mergemenu将所有菜单项合并成树形结构。每个key对应一个list,它是按照order进行排序的。后面就是输出菜单结构。目前submenu还没有实现。
那么在admin_layout.html定义了菜单区的显示块和导入了menu()函数,这块可以认为是预提供的。用户要关心的是创建子模板,覆盖这个块和调用menu()函数,如admin_globals.html中的代码:
{{extend "admin_layout.html"}}
{{block nav_menu}}
{{<< menu("View Global")}}
{{end}}
从admin_layout.html继承,然后重定义了nav_menu块,后面是输出菜单内容。
在前面,架子都已经搭好了,剩下就是菜单是如何被定义的。它们是在view中。打开views.py看一下:
from uliweb.contrib.admin.menu import bind_menu
@expose('/admin')
@bind_menu('Settings', weight=10)
def admin_index():
return {}
我只选取了admin_index()的例子。因为每个菜单项最终是与某个url相绑定的,而view函数通过url_for可以反向得到url,所以直 接通过decorator将菜单项与某个view函数绑定是比较简单的做法。当然,你可能不喜欢这样的方式,你可以自由进行设计,毕竟这只是uliweb 的admin的一种设计,在进行你自已应用的开发时,你可以选择你喜欢的方式。bind_menu()有几个参数,在上面的例子中有些被省略掉了。函数目 前的原型是:
bind_menu(caption, id=None, parent_menu_id=None, weight=10)
意思比较简单就不再解译了。
因此如果你想创建自已的app并且在admin中展示出菜单,你需要:
1. 为你的view函数添加bind_menu()的处理,以增加菜单。
2. view对应的模板需要从admin_layout.html继承,并覆盖nav_menu块,添加menu(current)的调用以显示菜单。menu()函数的参数表示当前菜单id。
未来类似的方式可能会用在其它可以扩展的地方,只是形式和内容会有所差别。当然这里还有一些可以扩展的地方,比如增加对内容的缓存处理。另外就是以可视化的方式对这些动态数据进行修改并保存,就象drupal所做的那样。不过这些是有一定的难度的。
Zeuux © 2024
京ICP备05028076号
暂时没有评论