博客首页|TW首页| 同事录|业界社区
2011-03-09

接着 上文 的内容,本文将基于自己的理解尝试回答 上文 中研究列表中的3和4,即:

  1. 理解和分析Admin系统的设计和代码书写值得学习和注意的问题
  2. 总结

理解和分析Admin系统的设计和代码书写

Admin系统具有多种特征,首先它是一个普通的 Django 应用,其次它又承担 作为各个其它Djago应用的基础应用而存在,所以我们可以从上面两个方面 来加以分析。

作为一个普通的应用

Django 本身是一个基于MVC的web框架,各个层次非常清楚,当然在 Django 中,更多地被 称为MTV,即Model, Template, View,对应于MVC的Model, View, Controller。 其中Template和View的衔接是通过url映射完成,而url映射也是 Django 架构中最为巧妙的 地方。

http://towerjoo.blog.techweb.com.cn/wp-content/blogs.dir/14215/files/blog/django_layers.png对于一个 Django 的应用,我们通常可以从url映射入手,找到url处理的函数,和渲染出的页面, 从而理清整个的应用逻辑。

Django 的Admin应用的url映射大致如下:

# sites.py
urlpatterns = patterns('',
    url(r'^$',
        wrap(self.index),
        name='index'),
    url(r'^logout/$',
        wrap(self.logout),
        name='logout'),
    url(r'^password_change/$',
        wrap(self.password_change, cacheable=True),
        name='password_change'),
    url(r'^password_change/done/$',
        wrap(self.password_change_done, cacheable=True),
        name='password_change_done'),
    url(r'^jsi18n/$',
        wrap(self.i18n_javascript, cacheable=True),
        name='jsi18n'),
    url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
        'django.views.defaults.shortcut'),
    url(r'^(?P<app_label>\w+)/$',
        wrap(self.app_index),
        name='app_list')
    )

# Add in each model's views.
for model, model_admin in self._registry.iteritems():
    urlpatterns += patterns('',
        url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
                include(model_admin.urls))
    )

从上面的代码,我们可以看到,Admin应用的landing page,登出,密码修改等相应的逻辑, 重点查看最后几行代码,将已经注册的应用的model(数据库的表)相应url映射指向了其自身的方法。

我们来看model_admin.urls这个逻辑:

urlpatterns = patterns('',
    url(r'^$',
        wrap(self.changelist_view),
        name='%s_%s_changelist' % info),
    url(r'^add/$',
        wrap(self.add_view),
        name='%s_%s_add' % info),

    # added by Tower Joo At 2011/2/27
    # have to be ahead of change_view unless it will be overrided
    url(r'^export/$',
        wrap(self.export),
        name='%s_%s_export' % info),
    url(r'^aggregate/$',
        wrap(self.aggregate),
        name='%s_%s_aggregate' % info),

    url(r'^(.+)/history/$',
        wrap(self.history_view),
        name='%s_%s_history' % info),
    url(r'^(.+)/delete/$',
        wrap(self.delete_view),
        name='%s_%s_delete' % info),
    url(r'^(.+)/$',
            wrap(self.change_view),
            name='%s_%s_change' % info),
    )

从上面的url映射来看,已经包含了增加记录,删除记录,修改记录等操作,当然上面也包含了 在 上文 中我做的一些url映射的修改。

看过上面的url映射后,心里就会有数,知道整个Admin系统大致有多少个页面,各个页面的作用是什么。

然后,我们可以通过url映射知道处理这个url的函数,例如,处理/add/的self.add_view函数。 通过阅读self.add_view函数的代码,除了相比于我们通常的逻辑更多的边界和条件处理外,其它的 也是相同的,例如数据库的操作,页面渲染所需要的数据准备等。

最后,我们看到的是页面渲染所需要的Template, change_form.html,查看对应的代码,则与通常的
Django 的Template并无二异。

其它的逻辑也相同。相比于我们自己的 Django 应用,这个逻辑中有更多的边界条件的处理和异常的处理 等,你可能会说它过于复杂,这也就引入了我们接下来要说明的第二个问题。

作为其它应用的通用基础应用

作为一个基础的应用,它就需要适应所有基于其上的其它应用的需要。 通用性通常意味着更多的复杂性,我们来看 Django 是如何有效且优美地处理这个问题的。

  1. 充分使用Python的内省,也即model的元数据,如app_label, module_name等,使得动态地构造url映射 和合适的显示成为可能
  2. admin = AdminSite()是一个全局的变量,来维护所有的注册应用的列表
  3. 两级的处理结构:admin site级和table级,分别由两个类来处理,并完成相应的url映射
  4. 可配置性:对于其上层的应用,都提供了完善的可override的属性,如list_display,list_filter等等
  5. 使用类而非 Django 默认推荐的函数作为view的处理,这样就提供了用户基于Admin系统建立自己的Admin系统的可能

总结

Admin系统可谓是 Django 最强大的功能了,它也大大方便了数据库驱动的应用的开发难度,为应用的数据输入,管理等 提供了一个稳定,可靠,方便的管理界面.

从 上文 和本文的介绍中,我们可以从中学习到一些 Django 常用的开发技巧:

  1. 基于类的view实现(当然在 Django 已经完全支持class view了, 参考 Class-based generic views )
  2. 分级的数据处理
  3. 可配置性
  4. 灵活性

会使用 Django 的Admin系统那可以让你的工作时间节省30%以上(基于数据库的应用),如果能够弄清楚 Django Admin 的实现原理并从中学习到可用于自己实际开发的经验与方法,则可运筹帷幄,谈笑风生地写代码了.

2011-02-28

Django 的Admin系统是 Django 几大killer feature之一, 所以对它的一些研究对于熟悉 Django 及其应用都有重要价值(因为Admin系统 是一个完整的 Django 应用示例).

http://towerjoo.blog.techweb.com.cn/files/2011/02/django_logo.png

本次大致的研究过程如下:

  1. 将 Django 的Admin系统拷贝出来,在一个新的 Django 项目中作为独立应用来运行
  2. 对这个独立的应用进行一定的修改
  3. 理解和分析Admin系统的设计和代码书写值得学习和注意的问题
  4. 总结

相关的代码可在 github 查看和检出,也可 下载zip后的代码 。 (techweb会过滤掉一些字符导致无法正确显示和访问,正在协调解决)

将Django的Admin系统作为独立应用来运行

  1. 使用django-admin.py startproject myadmin新建一个新项目
  2. 将django的原文件拷贝到新项目myadmin目录下
  • 找到django的安装目录, 如果不知道,可在python的交互式prompt下输入:
  • import django
  • print django.__file__
  • 那么 Django 的Admin应用就在DJANGO/contrib/admin/
  • 将整个目录拷贝到myadmin目录下
  • 如果需要,则调整相关的文件权限
  1. 修改myadmin项目中的settings.py和urls.py
  2. 同步数据库(syncdb)和运行代码

这时我们的第一个目标得以实现,因为根据Python语言package的导入规则(或者叫module search规则), 相对引入(relative import)会覆盖系统的django module,所以myadmin项目中运行的是myadmin下的 admin项目,而不是系统安装的。

为了检验,我们不妨可做一个简单的修改来证明,我们使用的是myadmin下的admin,我们在myadmin/django/contrib/admin/sites.py 的248行加入一句: import logging; logging.debug(”My Edit here”)

使用浏览器请求http://127.0.0.1:8000/admin/, 则你可在输出的log中看到上面的log.

对Admin系统进行一定的修改

我们打算做以下几点修改:

  1. 在一个具体的table的显示页面,增加导出csv的功能(也就是将当前查看表的数据导出为csv)
  2. 在一个具体的table的显示页面,增加快捷的聚合操作的支持(如sum,average等)

修改1

通过阅读代码,大致的修改步骤如下:

  1. 找到显示页面对应的template(change_list.html),也可使用方便的debug_toolbar来查看
  2. 添加导出为csv的链接
  3. 增加相应的url映射(options.py第254行)
  4. 增加相应的处理(options.py第773行)

相应的csv逻辑还是比较简单的,当然我只实现了将此表中所有记录导出的功能,部分数据导出的功能并未实现。

在实现的过程中,找到相应的url映射和及处理逻辑是比较重要,和相对较难的,大致的过程如下:

  1. 入口是: admin.sites.urls,发现其是一个property
  2. 继而找到get_urls方法,得到相应的url映射,发现对于特定model的url映射是对应各个model的urls属性
  3. 继而找到options.py文件,同样发现其是一个property
  4. 继而找到get_urls方法,及其相应的url映射,这里便是我们要添加我们的url映射的地方
  5. 然后加入相应的url映射的处理逻辑即可

从上面的代码我们会发现django的逻辑很清楚,而且层次分割和明确。

修改2

基本的步骤同修改1,只是在功能和界面上有所调整。

在修改2的完成中,我们使用了

  1. forms
  2. database aggregation

最终的结果可见下图:

http://towerjoo.blog.techweb.com.cn/files/2011/02/django_admin_result1.png

分析与总结

请查看下篇文章