原创作者: huangyiiiiii   阅读:1861次   评论:1条   更新时间:2011-06-01    
又用 django 做了个项目,因为主要都是后台的东西,所以决定启用 django 的 newforms admin 分支!(不过这里我不是推荐大家现在就开始用 newforms admin 分支,如果没有把握,最好是抱着玩玩的态度先,我在开发过程中就改掉它好几个bug)

newforms admin 分支是用 newforms 来重构 admin 模块,也顺便改变了一些设计决策,大大增强了 admin 的可定制性。首先 newforms 的应用,成功分离了 db field、form field、widget 三个部分,db field 属于 ORM ,主要负责 model 相关的事务,form field 主要处理用户输入数据的验证,widget 负责渲染ui,似乎这里面还透着 MVC 的影子呢 ;-)
newforms admin中可以方便地对 widget 进行替换,怎一个爽字了得。
另外,新的 admin 把 admin 部分的定义从 model 中分离出来了,似乎写起来要麻烦点,不过好处也是显而易见的,首先是 model 定义更整洁了,其次新的 admin 设计成了一种重用性更好的形式,用得好的话还能省下不少代码呢,而且能够完成一些以前的 admin 很难完成的任务。
新 admin 的核心在于 AdminSite 和 AdminModel,AdminSite 负责一些全局性的事务,比如首页,用户登录登出改密码权限控制,和model的注册,AdminModel 负责单个 model 的相关管理页面。 这样做的好处是你可以继承这两个类,覆盖掉一些合适的方法,你基本上可以为所欲为。
比如,我在这个项目中就写了这么几个自定义的 admin 类:

class CustomAdmin(admin.ModelAdmin):
def before_save(self, request, instance, form, change=False):
pass

def save_add(self, request, model, form, post_url_continue):
def custom_save(form, commit=False):
instance = model()
new_object = forms.save_instance(form, instance,
fail_message='created', commit=False)
self.before_save(request, new_object, form)
if commit:
new_object.save()
for f in model._meta.many_to_many:
if f.name in form.cleaned_data:
setattr(new_object, f.attname, form.cleaned_data[f.name])
return new_object
form.__class__.save = custom_save
return super(CustomAdmin, self).save_add(request, model, form,
post_url_continue)

def save_change(self, request, model, form):
def custom_save(form, commit=False):
from copy import copy
new_object = forms.save_instance(form,
copy(form.original_object),
fail_message='changed', commit=False)
self.before_save(request, new_object, form, change=True)
if commit:
new_object.save()
for f in model._meta.many_to_many:
if f.name in form.cleaned_data:
setattr(new_object, f.attname, form.cleaned_data[f.name])
return new_object
form.__class__.save = custom_save
return super(CustomAdmin, self).save_change(request, model, form)

大家应该可以看得出来,这个 admin 提供了 before_save 的钩子(当然你也可以提供 after)save 不过我这里暂时只需要 before_save),你可以继承它然后在这个方法里写些代码,就得在 model 保存之前得到执行。你可能要问,为什么不直接定义 Model 的 save 方法呢?答案很简单 Model 不知道 request 和 form 的存在!
在 before_save 中你就可以做些很有意思的事情了,比如自动把 model 中某个字段设置成当前登录用户!这个定制需求其实很早就提出来了,以前的解决方案是写个 middleware 把 request 放到 threadlocal 中去,然后在 model 中通过 threadlocal 获取当前请求的 request ,能用,但是很麻烦也很丑。现在用这个 before_save 可以轻松实现:
class AutoUserAdmin(CustomAdmin):
user_field_name = 'postuser'
def before_save(self, request, instance, form, change=False):
setattr(instance, self.user_field_name, request.user)
super(AutoUserAdmin, self).before_save(request, instance, form, change)

当然你也可以继承这个 AutoUserAdmin ,写上你自己的 user_field_name ,太简单了。
还有一个常见的定制需求就是限制登录用户只能看到自己发布的信息,看不到更不能修改别人发布的信息。 在上面这个 AutoUserAdmin 的基础上做:
class RestrictUserAdmin(AutoUserAdmin):
def queryset(self, request):
queries = {self.user_field_name:request.user}
return super(RestrictUserAdmin, self).queryset(request).\
filter(**queries)

是不是超简单?呵呵。
另外别忘了 python 还支持传说中的多重继承,意味着你可以同时继承多个 admin 类,并拥有多个 admin 类的组合功能。比如我这里定制了一个支持文件上传的 admin(newforms 和 newforms admin 暂时都还没有把文件上传相关的东西加进去,只能自己写),我把它叫做 FileUploadAdmin ,现在我希望我的 admin 能同时拥有 RestrictUserAdmin 和 FileUploadAdmin 的功能,没问题:
class CommonAdmin(FileUploadAdmin, AutoUserAdmin):
date_hierarchy = 'pubdate'
list_per_page = 15
ordering = ('-id',)

当然我还在里面定义了一些通用的(当然是对于我自己的项目来说) admin 配置。
然后怎么把这些 admin 应用到 model 上去呢?
class ProductAdmin(CommonAdmin):
list_display = ('__str__', 'type', 'unitname', 'unitprice',
'qsinfo', 'postuser', 'pubdate', 'image_view')
list_filter = ('type', 'pubdate')
)
admin.site.register(Product, ProductAdmin)

上面的代码虽然不错,不过我还是嫌麻烦,实际上我是这么写的:
admin.site.register(Product,
CommonAdmin,
list_display = ('__str__', 'type', 'unitname', 'unitprice',
'qsinfo', 'postuser', 'pubdate', 'image_view'),
list_filter = ('type', 'pubdate'),
section_name = '通用',
)

不过要让上面的代码正常运行,还需要对 django newforms admin 分支的代码做一点小改动即可,在文件 django/contrib/admin/sites.py 中大约 73 行的位置,
          # TODO: Handle options

的下面加上:
          # it works
if options:
admin_class = type(admin_class.__name__, (admin_class,),
options)

实际上,使用 django 乃至 python 最大的快乐就是别人写的代码你都可以轻松看懂,这难道不是作为程序员最大的快乐吗? ;-)

如果你现在开始用 django newforms admin 分支的话,估计遇到的大部分问题都是和 unicode 有关(因为我遇到的就是这样的),这是因为目前 django 的开发 和 python 本身的开发一样,都处在整体向 unicode 迁移的过程之中,当前最大的矛盾就是 ORM 使用的是普通字符串(也就是 python3000中所谓字节数组),而 newforms 却开始整体使用 unicode 了,这常常带来麻烦。如果你在基于 django 最新的 svn 版本开发,那一定要看一下 Unicode 分支了,里面说到了如何使让你的程序顺利过渡到 unicode ,祝大家过渡快乐 ;-)
评论 共 1 条 请登录后发表评论
1 楼 xiaorui1119 2010-11-26 10:23
  

发表评论

您还没有登录,请您登录后再发表评论

文章信息

Global site tag (gtag.js) - Google Analytics