现在用户单击 Save 时,他们将被指向预览页面,而不是展示所有 Documents 的列表页面。
使用 signals 向 admin 添加功能
signals 是 Django 中较少使用的功能,它可以提高代码的模块化程度。signals 定义保存模型或加载模板的事件,无论它在哪里运行,Django 项目都可以侦听到并对它做出反应。这意味着您可以轻松的提高应用程序的行为,而无需直接修改它们。
admin 提供了一个应用程序开发人员经常想修改的功能:通过 django.contrib.auth.models.User 类管理用户。Django 用户往往只能添加或修改 admin,这使得这个有用的类很难定制。
想象一下,您希望每次创建一个新的 User 对象时,站点管理员都能收到一封电子邮件。因为 User 模块无法直接在项目中使用,实现该目标的唯一方法似乎是子类化 User 或者使用间接方法,比如创建虚拟配置文件对象进行修改。
清单 11 展示了在保存 User 实例时添加运行的函数有多么简单。signals 通常被添加到 models.py。
清单 11. 添加新用户时使用 Django signals 进行通知
from django.db import models from django.db.models import signals from django.contrib.auth.models import User from django.core.mail import send_mail class Document(models.Model): [...] class Comment(models.Model): [...] def notify_admin(sender, instance, created, **kwargs): '''Notify the administrator that a new user has been added.''' if created: subject = 'New user created' message = 'User %s was added' % instance.username from_addr = 'no-reply@example.com' recipient_list = ('admin@example.com',) send_mail(subject, message, from_addr, recipient_list) signals.post_save.connect(notify_admin, sender=User) |
post_save signal 由 Django 提供,每次保存或创建模型时都会激活。connect() 方法带有两个参数:一个回调参数(notify_admin)和 sender 参数,后者指定该回调只关注 User 模型的保存事件。
在回调中,post_save signal 传递发送方(模型类)、该模型的实例和提示是否刚刚创建了实例的布尔值。在本例中,如果创建了 User,该方法将发送一封电子邮件;否则不执行任何操作。
有关其他 Django 提供的 signals 列表,请参见 参考资料,以及介绍如何编写 signals 的文档。
进一步修改:添加低级权限
|
一个常用的 Django admin 特性是它的权限系统,该系统可以扩展以包含低级权限。默认情况下,admin 可以细粒度控制角色和权限,但是这些角色仅应用于类级别:用户可以修改所有 Document 或不修改任何 Document。
往往只允许用户修改特定的对象。这常常称为低级 权限,因为它们只能修改特定数据库表格行,而综合权限可以修改表格中的任何记录。样例应用程序中的情况可能是您只希望用户看到他们创建的 Document。
首先,更新 models.py 以包含创建 Document 的属性记录,如下所示。
清单 12. 更新 models.py 以记录创建每个 Document 的用户
from django.db import models
from django.db.models import signals
from django.contrib.auth.models import User
from django.core.mail import send_mail
class Document(models.Model):
name = models.CharField(max_length=255)
text = models.TextField()
added_by = models.ForeignKey(User,
null=True, blank=True)
def get_absolute_url(self):
return 'http://example.com/preview/document/%d/' % self.id
def __unicode__(self):
return self.name
[...]
|
接下来,需要添加代码自动记录哪个用户创建了 Document。signals 无法用于这种情况,因为 signal 没有访问用户对象。但是,ModelAdmin 类提供了一个方法,使用该请求和当前用户作为参数。
修改 admin.py 中的 save_model() 方法,如下所示。
清单 13. 重写 DocumentAdmin 中的方法,以在创建数据库时保存当前用户
from django.contrib import admin class DocumentAdmin(admin.ModelAdmin): def save_model(self, request, obj, form, change): if getattr(obj, 'added_by', None) is None: obj.added_by = request.user obj.last_modified_by = request.user obj.save() [...] |
如果 added_by 的值是 None,那么这就是一个尚未保存的新记录。(您还可以检查 change 是否为 false,这指示是否将添加记录,但是检查 added_by 是否为空表示是否填写添加到 admin 之外的记录)。
另一个低级权限是将文档列表限制在创建它们的用户范围内。ModelAdmin 类为此提供了一个钩子程序 —— 它有一个名为 queryset() 的方法,该方法可以确定任何列表页面返回的默认查询集。
如清单 14 所示,重写 queryset() 以便将清单限制在当前用户创建的这些 Document 中。超级用户可以看到所有文档。
清单 14. 重写列表页面返回的查询集
from django.contrib import admin from more_with_admin.examples import models class DocumentAdmin(admin.ModelAdmin): def queryset(self, request): qs = super(DocumentAdmin, self).queryset(request) # If super-user, show all comments if request.user.is_superuser: return qs return qs.filter(added_by=request.user) [...] |
现在,任何对 admin 中 Document 列表页面的查询只显示当前用户创建的文档(除非当前用户是高级用户,这种情况下将显示所有文档)。
当然,只要知道 ID,用户就可以访问编辑页面查看未授权的文档,当前对此没有任何阻止手段。真正安全的低级权限需要重写更多方法。因为 admin 用户通常都达到某种水平的信任,所以有时一些基本的权限便足以提供精简的工作流了。
结束语
定制 Django admin 不需要 admin 源代码知识,也不需要修改源代码。admin 可以使用普通的 Python 继承和一些 Django 特有功能(比如 signals)进行扩展。
通过创建全新管理界面定制 admin 的优点很多:
- 保证活动开发持续进行的同时,您的应用程序可以从 Django 的优势中受益。
- admin 已经支持大部分通用用例。
- 添加到项目的外部应用程序可以自动与代码并排管理。
在 Django V1.1 中,admin 提供了两种常用的新功能:在列表页面编辑内联字段和 admin 动作,后者允许一次批量更新许多项目。这两个新功能都不需要从头编写通用的功能,并且为其他定制操作提供了扩展点。(责任编辑:A6)