李迎辉

李迎辉的博客

他的个人主页  他的博客

在admin中实现可扩展菜单

李迎辉  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哲思注册吗?现在 注册 !

暂时没有评论

Zeuux © 2024

京ICP备05028076号