编写你的第一个 Django app,第七部分(Page 12)转载请注明链接地址
本节教程承接第六部分(page 11)的教程。我们继续开发 web-poll应用,并专注于自定义django的自动生成的admin站点,这点我们在第二部分(page 7)中探讨过。
自定义admin表单
通过在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类的模型,然后把它传递给admin.site.register()
的第二个参数 —— 当你需要为一个模型修改admin选项时。
上面的更改使“Publication date”字段出现在“Question”字段之前:
这里仅有两个字段,给人的印象不够深刻,但对于有几十个字段的admin表单来说,选择一个有直观顺序则是一个有重要作用的细节。
说到有几十个字段的表单,有可能会想把表单分割成字段集合:
# 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
中每个元组的第一个元素是字段集合的标题。这是我们的表单现在的外观:
添加关联对象
现在我们有了Question管理页面,但是一个Question
有多个Choice
,并且管理页面不显示choice。
然而,有两种方式解决这个问题。第一种是在admin中注册choice
,就像我们之前对Question
做的一样。这很简单:
# polls/admin.py
from django.contrib import admin
from .models import Choice, Question
# ...
admin.site.register(Choice)
现在在django 的admin中 “Choices” 是有一个有效的选项。 “Add choice”表单就像下面的样子:
在这个表单中, “Question” 字段是一个包含数据库中的各个问题的选择框。django知道 ForeignKey
(这里少一个链接)对应admin中的<select>
框。在我们的例子中,此时只有一个quesiton存在。
同时要注意链接到下一个”Question“的 “Add Another”链接。每个和其他对象有ForeignKey
(这里少一个链接)关系的对象都有这个功能。当你点击了“Add Another”,会出现一个有“Add Another”表单的弹出窗口。如果你在这个窗口中添加一个question并点击“Save”,Django会把这个question保存到数据库,并把它作为一个可选择的choice,动态添加它到你看到的“Add choice”表单中。
但是,实际上,这是一种向系统中添加Choice
对象的很低效的方式。更好的方法是,在你创建Question
对象的时,直接添加许多Choice
。让满五年来实现它。
从 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个choice提供足够的字段。”
加载“Add question”页面,看看它现在是什么样子:
它的工作方式是这样的:有三个有联系的Choice —— 由extra
指定 —— 并且每次你回到一个已创建对象的 “Change” 页面,你都会得到三个空白的Choice。
在当前三个Choice的底部,你会看到一个 “Add another Choice” 链接。如果你点击了它,会添加一个新的Choices。如果你想一出一个已添加的Choice,你可以在已添加的Choice中的右上角点击 X。注意,你无法移除最开始的三个Choice。这张图展示了一个添加的Choice:
还有一个小问题,为了显示关联的Choice
对象,大量的屏幕空间用来显示所有的字段。因为这个原因,Django提供了以表格的形式显示内嵌关系的对象;你只需要修改ChoiceInline
的声明即可:
# polls/admin.py
class ChoiceInline(admin.TabularInline):
#...
使用TabularInline
(替代了StackedInline
),关联的对象显示成更紧凑的、基于表格的格式:
注意有一个额外的“Delete”列,它可以移除使用“Add Another Choice”按钮添加并保存的行。
自定义admin变更列表
现在问题管理页面看起来看好,我们在对“change list”页面做一些小改动 —— 该页面显示系统中的所有Quesiton。
下面是它现在的样子
默认情况下,Django显示每个对象的str()
。但有时候,如果我们显示单个字段会更有帮助。要实现这个功能,可以使用list_display
(这里少一个链接)管理选项,它是一个字段名的元组,在change list页面像列一样显示:
# polls/admin.py
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date')
只是为了更好一些,我们把第二节中的was_published_recently()
也加入进来:
# polls/admin.py
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date', 'was_published_recently')
现在问题change list 页面看起来会像下面:
你可以点击列的头部来以这个值来排序 —— 除了本例中was_published_recently
的头部,因为不支持以随意一个方法的输出来排序。另外要注意的是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_display
(这里少一个链接)。
再次编辑polls/admin.py
文件,对Question
change list页面做一个改进:使用list_filter
(这里少一个链接)添加过滤器。把下面的行添加到QuestionAdmin
中:
list_filter = ['pub_date']
这样就添加了一“Filter”侧边栏,可以让人通过pub_date
字段对change list进行过滤:
过滤器的显示类型依赖于你使用的过滤字段的类型。因为pub_date
是一个DateTimeField
(这里少一个链接),django会给出合适的过滤选项: “Any date”, “Today”, “Past 7 days”, “This month”, “This year”.
现在一切进展顺利。我们添加一些搜索功能:
search_fields = ['question_text']
这样会在change list顶部添加一个搜索框。当有人输入搜索内容,django会搜索question_text
字段。你可以许多你喜欢的字段 —— 虽然它在后台使用一个LIKE
查询,将搜索字段的数量限制为合理的数目,会让你的数据库在搜索时更容易。
现在还是一个告诉你change list提供方便的分页功能的好机会。默认每页显示100项。 Change list pagination(这里少一个链接), search boxes(这里少一个链接), filters(这里少一个链接), date-hierarchies(这里少一个链接), column-header-ordering(这里少一个链接) 它们会按照你认为的那样一起工作。
自定义admin的外观
很明显,在每个admin页面的顶部显示“Django administration”会显得很愚蠢。它仅起到了占位符文本的作用。
尽管,使用django的模板系统这个很容易改变。django damin是有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)。我们会在reusable apps tutorial中讨论我们为什么要这样做。
现在在 templates中创建一个名字是admin的目录,并从django自己的安装目录中(django/contrib/admin/templates)的的django admin模板目录中把admin/base_site.html复制到这里来。
django的安装目录在哪? 如果你找不到django安装目录在你文件系统中的位置,可以运行下面的命令
$ python -c "import django; print(django.__path__)"
然后,只要编辑并把 {{ site_header|default:_('Django administration') }}
(包括花括号)替换成你自己的想要使用的站点名。你会得到一段类似下面的代码:
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1>
{% endblock %}
我们使用这个方法教你如何重写模板。在实际的项目中,你可能会使用django.contrib.admin.AdminSite.site_header(少一个链接)属性来更简便的实现这个功能。
这个模板中包含大量类似**{% block branding %}** 和 {{ title }} 的文本。 {% 和 {{ 标签是django模板语言的一部分。当django渲染admin/base_site.html 时,模板语言会评估以生成最终的HTML页,就像我们在第三部分中看到的一样。
注意,任何django admin模板都可以被重写。要重写一个模板,和重写base_site.html的操作一样 —— 从默认目录中把模板文件复制到你指定的目录中,并修改即可。
自定义你的应用的模板
细心的读者可能会问:但如果DIRS(少一个链接)默认是空目录,django如何找到默认的admin模板?答案是,由于APP_DIRS(少一个链接)设置成了True,django会自动在每个应用包中查找一个templates/子目录,以作备用(不要忘记django.contrib.admin是一个应用。)
我们的poll应用不是很复杂,也不需要自定义admin模板。但如果它变得越来越复杂,并因为一些功能需要修改django的标准admin模板,明智的方法是修改应用的模板,而不是项目中的模板。使用这种方式,你可以在任何新项目中使用polls应用,并确保它可以找到它需要的自定义模板。
更多关于Django如何找到它的模板请查看template loading documentation(少一个链接)
自定义admin默认页
和上面类似的是,你可能想自定义django admin默认页的外观。 默认情况下,它会按照字母的顺序显示INSTALLED_APPS中所有在admin应用注册过的应用。你可能会想对布局做一个大的更改。毕竟,默认页可能是admin中最重要的页面,它应该易于使用。
要自定义的模板是admin/index.html。(和前面对admin/index.html做的 操作一样 —— 从默认目录中复制它到你的自定义模板目录)。编辑这个文件,你会看到它使用了一个叫app_list的模板变量。这个变量包含所有已安装的django app。你可以用你认为最好的方式将链接硬编码到特定对象的admin页面,来替代这个变量。
下一步做什么
初学者教程到此结束。在此期间,你可能想要去有一些快速链接 where to go from here. 如果你熟悉python打包技术,并对如何将一个app变成一个“可重用的app”,请查看Advanced tutorial: How to write reusable apps(少一个链接)
来源:oschina
链接:https://my.oschina.net/u/4411583/blog/4228802