这个教程紧跟教程(6)。我们将继续网页问卷调查应用并且将聚焦在定制Django的自动生成管理站点,这在教程(2)的时候我们有过首次接触。
1. 自定义管理形式
通过使用admin.site.register(Question)来注册Question模型,Django能组成一个默认的窗体展示。通常,你将想要自定义管理站点的外观和行为。当注册对象的时候你可以通过告诉Django你想要的选项来完成这个。
让我们看看通过在编辑窗体上重排序是如何工作的。用下面的代码替换这一行admin.site.register(Question):
polls/admin.py
from django.contrib import admin
from .models import Question
class QuestionAdmin(admin.ModelAdmin):
fields = ['pub_date', 'question_text']
admin.site.register(Question, QuestionAdmin)
你遵照这种形式 -- 创建一个模型管理类,然后将它作为第二个参数传递给admin.site.register() -- 任何时候你需要改变一个模型的管理选项。
上面的特别的修改让“发布时间”出现在“Question”字段之前:
只有2个字段并不会让人印象深刻,但是对于有几十个字段的管理窗口来说,选择一个直观的顺序是一个重要的可用细节。
说到几十个字段的窗体,你也许想要将窗体拆分成字段集:
polls/admin.py
from django.contrib import admin
from .models import Question
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date']}),
]
admin.site.register(Question, QuestionAdmin)
在fieldsets中的每个元组的第一个元素是fieldset的标题。这就是我们窗体现在的样子:
OK,我们有了自己的管理页面,但是一个Question有多个Choices,但是管理站点没有显示choices。
仍然。
有2种方式来解决这个问题。第一个是像我们注册Question一样在admin中注册Choice。那很简单:
polls/admin.py
from django.contrib import admin
from .models import Choice, Question
# ...
admin.site.register(Choice)
现在“Choices”在Django管理里是一个可选项了。添加“choice”的窗体是这样的:
在那个窗体中,“Question”字段是一个选择框,它包含了数据库中的每个问题。Django知道ForeignKey在管理中应该作为一个
同样要注意“Add Another”链接紧挨着"Question".每个和另外一个有ForeignKey关系的对象都能免费得到这个。当你点击"Add Another",你将会得到一个带有“Add Question”窗体的弹出窗口。如果你在哪个窗口添加了一个问题并且点击了"save",Django将会保存这个问题到数据库并且动态的添加它到你正在看的"Add choice“窗体的可选choice。
但是,事实上,这是添加choice到系统的低效方式。好点的方式是当你在创建Question对象的时候,你可以直接添加大量的Choices。让我们来实现这个。
移除Choice模型的register()调用。然后,像这样编辑Question注册:
polls/admin.py
from django.contrib import admin
from .models import Choice, Question
class ChoiceInline(admin.StackedInline):
model = Choice
extra = 3
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
]
inlines = [ChoiceInline]
admin.site.register(Question, QuestionAdmin)
这告诉Django:”choice对象在Question管理页面被编辑。默认情况下提供最多3个choices。“
加载”Add question“页面看看效果:
它像这样工作:对相关的Choices有3个插槽 -- 正如extra指定的一样 -- 并且每次你回到一个已经创建对象的”修改“页面,你将会得到另外3个额外的插槽。
在当前3个插槽的底部你将会发现一个”Add another choice“链接。如果你点击它,一个新的插槽将会被添加。如果你想要移除这个新添加的插槽,你可以点击添加插槽右上角的X。注意,你不能移除原始的3个插槽。下面的图片展示了一个添加的插槽:
一个小问题,尽管。它占用了太多的屏幕空间在输入相关choice对象的时候来显示所有字段。因为那个原因,Django提供了一个表格的方式来显示内联相关对象;你只需要修改ChoiceInline描述:
polls/admin.py
class ChoiceInline(admin.TabularInline):
#...
使用TabularInline(而不是StackedInline),相关的对象显示在一个更紧凑的表格式的格式:
注意这里有一个额外的”Delete?“列,它允许使用”Add Anoter Choice“按钮来移除已添加的行和已经保存过的行。
现在Question管理页面看起来不错了,让我们调整”修改列表“页面 -- 在系统中显示所有问题的那个。
这是它现在看起来的样子:
默认的,Django显示每个对象的str()方法。但是一些时候如果我们能显示自己的字段将会很有帮助。为了做到那个,使用list_display管理选项,它是一个包含要显示的字段名称的元组,作为列,在对象的修改列表页面:
polls/admin.py
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date')
为了更好的测评,让我们包括在教程(2)中的was_published_recently()方法:
polls/admin.py
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date', 'was_published_recently')
现在问题修改列表看起来是这样:
polls/models.py
class Question(models.Model):
# ...
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
was_published_recently.admin_order_field = 'pub_date'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
list_filter = ['pub_date']
search_fields = ['question_text']
明确的,在每个管理页面的顶部的”Django administration“是可笑的。它只是一个占位文本。
那很容易修改,尽管,使用Django的模板系统。Django管理是由Django本身驱动的,并且它的接口使用Django自己的模板系统。
在你的项目路径(包含manage.py的那个)下创建一个templates路径。模板能在你的文件系统的任何Django可以访问的地方。(Django作为你服务运行的任何用户来运行)然而,在项目中保持你的模板是一个良好的习惯。
打开你的设置文件(mysite/settings.py,记得)并在TEMPLATES设置中添加一个DIRS选项:
mysite/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
DIRS是一个文件系统路径的列表,用于检查什么时候加载Django模板;它是一个搜索路径。
组织模板
就像静态文件,我们可以将我们所有的模板一起放在一个大的模板目录下,并且它会运行的相当好。然而,属于一个特殊应用的模板应该被放在应用的模板路径(例如,polls/templates)而不是项目的(templates)我们将会在<复用应用教程>中讨论这些细节。
现在在templates里面创建一个admin的路径,然后将模板admin/base_site.html从Django源码的默认Django管理模板路径(django/contrib/admin/templates)拷贝到那个路径。
Django源码文件在哪里?
如果你找不到在你系统中的源码文件,运行下面的命令:
$ python -c "import django; print(django.__path__)"
然后,编辑那个文件,并且用你的站点名称来替换{{ site_header|default:_('Django administration') }}(包括花括号),你应该以这样一个代码来结束:
{% block branding %}
Polls Administration
{% endblock %}
我们使用这个方法来告诉你如何覆写模板。在一个真实的项目中,你可能用Django.contrib.admin.AdminSite.site_header属性来更容易的完成这个特殊的自定义。
模板文件包含很多的文本像{% block branding %}和{{ title }}。{% 和{{标签是Django模板语言的一部分。当Django渲染admin/base_site.html,这个模板语言将会评估来产生最终的HTML页面,就像我们在教程(3)中看到的一样。
注意任何Django默认的管理模板都可以被覆写。为了覆写一个模板,只要做你在base_site.html中做的一样 -- 从默认路径将它拷贝到你自定义路径然后修改。
聪明的读者也许会问:如果DIRS默认是空的,Django如何找到默认的管理模板?答案是,因为APP_DIRS被设置为True,Django自动在每个应用包里寻找templates/子路径,作为备用(不要忘记了django.contrib.admin是一个应用)。
我们的问卷调查应用不是非常复杂,并且不需要自定义管理模板。但如果它增长的复杂并且需要为了它的功能性对Django的白标准管理模板做一些修改,修改应用的模板将会是非常明智的,而不是项目的。那样,你可以将polls应用包含在任何新的项目并且确保它将会找到它需要的自定义模板。
关于Django如何找到它的模板的更多信息,请阅读<模板加载文档>
同样,你也许想要自定义Django管理首页的样式和感觉。
默认的,它显示所有在管理应用中的INSTALLED_APPS中注册的所有应用,以字母排序的方式。你也许想要对布局做重大的改变。毕竟,首页可能是管理的最重要的页面,并且它应该容易使用。
自定义的模板是admin/index.html。(如先前在admin/base_site.html中做的一样 -- 从默认路径中拷贝它到你自定义的模板路径)。编辑文件,你将会看到它使用一个模板变量app_list。这个变量包含每一个安装了的Django应用。作为使用它的代替,你可以硬编码链接到指定对象的管理页面以你认为最好的方式。