接着 教程 1 继续. 本节我们将继续完善 Web-poll, 重点在于Django自动生成的网站管理功能.
哲理
添加, 修改, 删除内容是一件非常乏味, 而且不需要任何创造性的工作. 正是出于这样的原因, Django才采用完全自动化的方式创建模型的管理界面.
Django是在新闻编辑室的环境下产生的, 在这里内容出版商与公共网站之间存在非常明显的差异. 网站管理员使用这套系统发布新闻故事, 事件, 体育成绩等等, 这些内容都是在公共的网站上展现的. Django通过为管理员提供统一界面来编辑新闻内容, 从而解决了这一问题.
Django自动生成的管理界面不对访问者开放, 是专门给网站管理员使用的.
Django内置的网站管理功能默认是不开启的, 这是个可选项. 开启这项功能需要完成如下3步:
取消 INSTALLED_APPS 配置中 "django.contrib.admin" 的注释
运行 python manage.py syncdb 命令. 因为在 INSTALLED_APPS 中添加了新的应用, 所以需要运行这个命令来更新数据库表.
取消 mysite/urls.py 文件中与admin相关联的3行. 这是一个URLconf文件, 在下一节教程中会深入讲解. 现在你只需知道它是URL与应用间的映射. 编辑过后的 urls.py 文件会像下面这样:
from django.conf.urls import patterns, include, url # Uncomment the next two lines to enable the admin: from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', # Examples: # url(r'^$', '{{ project_name }}.views.home', name='home'), # url(r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')), # Uncomment the admin/doc line below to enable admin documentation: # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: url(r'^admin/', include(admin.site.urls)), )
(加粗的那3行是需要取消注释的.)
下面我们要启动服务器然后访问管理页面
回想教程1中是这样启动服务器的:
python manage.py runserver
然后在本地域名后面增加”/admin/”, 比如, http://127.0.0.1:8000/admin/. 这样就能看到管理员登录界面:
与你所见一样吗?
如果呈现在你面前和上图中的登陆页面不同, 并且抛出如下的错误:
ImportError at /admin/
cannot import name patterns
...
可能是因为使用了与教程中不同的Django版本造成的. 因此你可以选择老版本的教程或者选择新版本的Django.
现在登录到管理系统吧, 记得在教程1中我们创建了一个超级用户吗? 如果你没有创建或者忘记了密码, 可以 再创建一个用户. 登录成功就可以看见Django内置的管理系统的主页:
如上图中所示, 有些内容是可以编辑的, 有工作组, 用户以及站点. 这些就是Django默认内置的核心功能.
我们的poll应用在哪里?主页面没有显示啊!
还需要一道工序:需要告知系统 Poll 对象需要管理界面. 为了完成此工序, 还需要在 polls 目录下创建 admin.py 文件, 文件内容如下:
from polls.models import Poll
from django.contrib import admin
admin.site.register(Poll)
重启服务器后就能看见变化. 通常每次修改文件后, 服务器会自动重新载入, 但是创建文件不会触发自动载入.
Poll 已经在Django中注册, 这样Django就知道需要把它也显示在主页面:
点击”Polls”进入poll修改列表页面, 本页展现数据库中的所有记录, 并且可以对其进行修改. 这里面也有我们教程1中创建的”What’s up?”:
点击”What’s up?”记录进行修改:
注意事项:
页面底部有更多的操作:
如果”Date published”的值与在教程1中创建poll时输入的值不相符, 很可能是由于没有为 TIME_ZONE 配置正确的值. 重新配置后, 刷新页面就可以正常展现了.
点击”Today”和”Now”快捷键就可以修改”Date published”属性值, 然后点击”保存并继续编辑”保存修改. 随后点击右上角的”历史记录”, 就能看到这个对象的全部修改记录, 还记录着什么时间, 哪个用户进行的修改.
开始之前先为一行代码也没写就能实现这样的效果惊叹一会儿吧, 仅仅是通过 admin.site.register(Poll) 配置项告知Django, 就可以为Poll模型生成相应的表单. 但是有时我们想自定义表单的外观以及逻辑, 也很简单, 在注册对象时配置相应的参数即可.
如果想修改原表单字段的顺序, 只要用下面这段代码替换原先的 admin.site.register(Poll)
class PollAdmin(admin.ModelAdmin):
fields = ['pub_date', 'question']
admin.site.register(Poll, PollAdmin)
需要修改对象管理选项的时候就可以这么做:创建一个实体管理对象, 然后作为第二个参数传给 admin.site.register() 即可.
上面配置的修改就使得”Publication date”移动到”Question”字段之前.
这种方式不限于两个字段间的交换, 还可以用于多个字段的排序. 一个直观的排序方式是一个重要的提升可用性的方法.
提到表单的多个字段, 你可能想要把多个字段归类到不同的字段集中, 如下
class PollAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question']}),
('Date information', {'fields': ['pub_date']}),
]
admin.site.register(Poll, PollAdmin)
fieldsets 中元组的第一个元素是字段集的名称. 我们的表单就变成下面的样子了:
可以为每个字段集指定class样式, Django内置了 "collapse" 样式, 用来指定字段集为折叠的. 当表单中包含大量不经常使用的字段时, 就可以用它把这个字段集折叠起来:
class PollAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question']}),
('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
]
OK, 现在我们已经有Poll的管理界面了. 另外 Poll 实体中有多个关联的 Choices , 但是管理界面中却没有显示出来.
有两种解决这个问题的方法. 一个是像 Poll 那样配置 Choice , 这个简单, 如下
from polls.models import Choice
admin.site.register(Choice)
这样 “Choices” 的选项就出现在管理界面上了, “Add choice”表单如下图:
在添加choice的表单中, “Poll”字段是一个包含数据库中所有”Poll”的一个下拉框, Django知道在页面展现是需要将 ForeignKey 映射为一个下拉框. 本示例中只有poll需要被映射为下拉框.
注意在”Poll”后面还有个”Add Another”链接, 没有有外键关联的对象都会自动生成这样一个链接. 点击”Add Another”链接后, 就会弹出添加poll对话框, 如果点击保存按钮, Django就会将poll保存到数据库, 并且动态添加到下拉框中.
但是这真的是一种效率很低的添加Choice的方法, 在添加Poll的同时添加多个Choice体验会好很多. 动手实现它!
首先删除Choice模型的 register() 配置, 然后编辑 Poll 的配置如下
class ChoiceInline(admin.StackedInline):
model = Choice
extra = 3
class PollAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question']}),
('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
]
inlines = [ChoiceInline]
admin.site.register(Poll, PollAdmin)
这样就告知了Django可以在Poll管理界面中编辑Choice, 默认情况下为Poll提供三个Choice编辑字段.
想看看修改后的添加poll界面时候起作用, 需要重启服务器.
如果看见三个空的关联Choice(通过 extra 属性指定的)就说明起作用了. 每次编辑不同已经添加的Poll时, 都会看到三个不同的关联Choice.
但是这样还是存在一个问题, 显示相关Choice对象占用了很多空间. 为了解决这个问题, Django提供了一种制表的方式来显示行内的关联对象, 只需要像下面那样修改 ChoiceInline 即可:
class ChoiceInline(admin.TabularInline):
#...
使用 TabularInline 替代 StackedInline 后, 这些关联对象显示的就很紧凑了. 基于表格的样式如下:
尽管Poll的管理界面已经很好了, 但是我们还是试着对修改列表页面做些调整吧.
现在的管理界面是这样的:
Django默认显示每个对象的 str() , 但是有些时候只需要显示特定的几个字段. 可是使用 list_display 管理选项达到这一目的, 它就是需要在修改列表页面展示字段的一个集合:
class PollAdmin(admin.ModelAdmin):
# ...
list_display = ('question', 'pub_date')
为了效果明显, 我们把教程1中自定义的 was_published_recently 方法也加进来:
class PollAdmin(admin.ModelAdmin):
# ...
list_display = ('question', 'pub_date', 'was_published_recently')
现在poll的修改列表页面就变成这样了:
可以点击表头进行排序, 由于不支持方法输出结果之后再进行排序, 所以点击 was_published_recently 表头是没法进行排序的, 而且默认情况下, was_published_recently 字段表头就是方法名称(把下划线用空格代替), 每行都显示方法执行结果的字符串形式.
为了避免这种不友好的情况, 可以在 was_published_recently 方法中添加几行代码, 如下:
class Poll(models.Model):
# ...
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
was_published_recently.admin_order_field = 'pub_date'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
然后再为Poll修改列表页面增加一个Filter. 为 PollAdmin 添加的Filter如下:
list_filter = ['pub_date']
添加的通过 pub_date 字段过滤记录的过滤器边栏如下:
右侧过滤边栏的展现取决于过滤字段的类型, Django知道应该为 DateTimeField 类型的 pub_date 字段的过滤器 边栏显示”Any date,” “Today,” “Past 7 days,” “This month,” “This year.”选项
过滤边栏就修饰完毕, 再加点儿搜索功能吧:
search_fields = ['question']
这样一个修改列表顶部的搜索框就加好了, 当输入搜索关键字后, Django就会通过关键字搜索 question 字段. 也可以通过配置搜索多个字段. 尽管后台是使用 LIKE 实现的, 但是只要保证合理性和相对高率即可.
因为Poll有日期字段, 为了方便通过日期筛选, 这一增加下面这行代码:
date_hierarchy = 'pub_date'
这样就在修改列表页面顶部增加了一个日期导航栏. 显示所有的年份, 然后精确到月与日.
值得注意的地方是, Django还自动为我们添加了分页功能, 默认每页显示100条记录. 现在分页, 搜索条, 过滤边栏, 日期导航以及表头排序和我们预想的那样展现在面前啦.
很明显, 每个管理页面的页头都显示”Django administration”是不恰当的, 他们只是系统名称的占位符, 可按需进行替换.
使用Django的模版系统很容易替换, Django管理系统是用Django框架实现的, 页面也是使用内置的模板系统.
打开配置文件(``mysite/settings.py``), 找到 TEMPLATE_DIRS 配置项, 它是加载Django模版时文件系统中路径的集合, 就是查找的路径.
TEMPLATE_DIRS 默认配置是空的, 所以要添加一行, 告知Django模板的所在位置:
TEMPLATE_DIRS = (
'/home/my_username/mytemplates', # Change this to your own directory.
)
把Django源码中(django/contrib/admin/templates)默认的 Django管理模板( admin/base_site.html )拷贝到配置文件中配置的目录. 比如配置文件中包含 '/home/my_username/mytemplates' , 那么就把 django/contrib/admin/templates/admin/base_site.html 文件 复制到 /home/my_username/mytemplates/admin/base_site.html 即可.
然后只要编辑这个文件, 把Django自动生成的名称换成适合你自己网站的名字.
模板文件中包含很多 {% block branding %} 和 {{ title }} 这样的文本. {% 和 {{ 都是Django模板语言的一部分. Django渲染 admin/base_site.html 页面时, 会用模板语言来生成最终的html页面. 别担心不会使用模板, 教程3中会深入介绍Django的模板语言.
任何Django内置的模板都是可以被替换的, 替换步骤和上面的步骤一样, 把自定义的模板复制到配置文件中指定的目录中即可.
细心的读者可能发现:默认 TEMPLATE_DIRS 是空的啊, Django是怎么找到默认模板的? Django会自动搜索每个app下面子文件夹的 templates/ 目录下的模板作为备用. template loader documentation 获取更多关于模板的信息.
本小节讲述如何自定义管理系统主界面.
默认 INSTALLED_APPS 中配置的应用是按字母顺序展现的, 主界面是管理系统中最重要的一个页面, 而且布局也应该易于使用, 所以为了这点很可能需要改变页面的布局.
需要像上一节那样修改 admin/index.html, 编辑这个文件的时候会发现它使用的是名字叫 app_list 的模板变量, 这个变量中包含了在Django中添加的应用. 不用这个也行, 也可以在页面中硬编码, 哪里放什么合适就在哪里硬编码. 再次强调, 不懂模板语言也没关系, 教程3我们会详细介绍模板语言.
达到满意的效果后, 阅读 教程3 , 关注创建poll的公共页面.
Dec 14, 2013