开始Django之旅-part7_自定义Django的管理页面
前言
这篇文正主要关注自定义Django的自动生成的管理员页面,在第二篇文章就涉及了一点。
这篇文章是该系列的最后一篇文章,最适合于新手来初次了解Django的全貌,但是对于您开发项目来说,了解的细节太少了。所以,在下决定,这篇文章结束之后,我们再开三个系列的模块:关于Django的model的一切、关于Django的view的一切、关于Django的templates的一切。这三个模板是开发项目的重中之重,我们前期学习就先把这三个模板的细节了解通透,然后再关注Django的测试、app的复用、migrations、Django的缓存框架等等。
自定义管理员的表单
通过使用admin.site.regiser(Question)注册Question模型,Django可能会创建一个默认的表格。通常情况下,你会想要自定义admin表格的样式和工作方式。当你注册对象的时候,你可以告诉Django你想要的操作,就可以了。
让我们重新编辑表单的顺序看一下它是如何工作的,编辑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”字段前面
这并不会很有印象,但是对于包含几十个字段的管理员表单来说,选择一个直觉的顺序是重要的细节。
说到几十个字段的表单,你可能会把表单划分为几个字段集合:
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)
打开网址http://127.0.0.1:8000/admin/polls/question/查看一下效果。
添加相关的对象
我们现在有了Question管理页面,但是一个问题包含了多个选项,但是管理页面并没有实现选项。
有两个方法解决这个问题。第一个方法是像刚才那样去注册Choice对象:
from django.contrib import admin
from .models import Choice, Question
# ...
admin.site.register(Choice)
现在“Choices”对象就显示在管理界面上了。
在表单上,“Question”字段是一个包含了任何问题的选择框。Django知道外键应该作为一个选择框在admin界面表示出来。
注意“Question”旁边的“添加另一个”链接。每一个带着外键关系的对象都会有一个这样的按钮。当你点击“Add another”,你就会打开一个“Add question”的弹窗表单。如果你在该窗口添加一个问题,点击保存。Django就会保存这个问题到数据库中,然后作为一个选中的选项显示在选择框内。
但是,这地狱添加Choice对象来说是没有效率的。如果当你创建一个Question对象是,就能直接添加许多Choices对象,就好了。让我们试一下吧。
移除Choice模型的register()调用。然后,编辑Question注册代码:
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”页面,看一下什么样子:
这里有三块地方用来创建Choice对象——因为extra具体了数量——每当你返回到了已经创建的Question对象的“Change”页面是,会有另外三块地方用来创建Choice对象。
左下角有一个“Add another Choice”链接。如果你点击了它,就会添加一个新的块。如果你想要移除它们,你可以点击块右上角的X。注意,你不能移除最初的三个块。添加一个块的样子是这样:
有一个小问题,想一下。它使用了大量的空间显示了Choice对象的所有字段。所以,Django提供了一个表格形式去显示对象。尝试一下:
polls/admin.py:
class ChoiceInline(admin.TabularInline):
#...
使用TabularInline代替了StackeInline,相应的对象就被显示在一个紧凑的、表格的表单上。
注意,这里有一个“Delete”的列,它可以移除已经保存的一行数据。
自定义管理员修改列表
现在,Question管理页面看起来比较不错了,让我们在“change list”上做一些修改——显示系统中所有的问题对象。
默认情况下,Django显示每个对象中str()函数返回的内容。我们也可以显示一些独特的字段。在admin选项中使用list_display,就是字段名字的元组,像列一样显示。
polls/admin.py:
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date')
还可以包含第二篇中的was_published_rencently()方法。
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date', 'was_published_recently')
现在,页面就是这样子:
你可以点击列头给他们排序——除了was_published_recently头,因为不支持按照任意方法的输出排序。同时,默认情况下,列名就是它的方法名(下划线换成了空格),每行包含了方法输出的内容。
你可以给该方法一些属性,例如:
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文件,使用list_filter添加一个过滤器:
list_filter = ['pub_date']
它在页面的右边添加了一个“filter”:
过滤器类型根据你设置的字段类型显示。因为pub_date是一个DateTimeFiled,Django知到给出合适的过去选项“任何时间”、“今天”、“过去7天”、“本月”、“本年”。
现在,让我们添加一个搜索功能:
search_fields = ['question_text']
这样就会在列表的顶部添加一个搜索框。当某人键入搜索词条,Django就会搜索question_text字段。你可以使用更多的字段——因为背后它用的就是一个like查询,限制搜索字段的数据到一个合理的数值,会使得你的数据库更容易查询。
现在,根据页码改变列表。默认每页显示100条数据。想看更多:修改列表的页码
,搜索框
,过滤器
,数据层级
和列头顺序
。
自定义管理员样式
确定的是,在每个管理页面顶部都有一个“Django administrator”,看起来有点搞笑。它其实就是个占位符。
你可以使用Django的模板系统修改它。Django的管理页面由Django自己驱动。它的接口使用Django自己的模板系统。
自定义项目的模板
在你的项目目录中创建一个templates目录(就是包含manage.py的目录)。模板可以放在Django可以访问的任何地方。
打开你的设置文件(mysiste/setting.py)添加一个Dirs选项:
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模板的时候就会检查,它是一个搜索路径。
组织的模板:就像静态文件一眼个,我们可以将所有的模板组合到一个大的模板目录里,它也会工作的很好。然而,属于某个特定app的模板应该放在该app的模板目录下,而不是项目的模板目录下。我们会在这里讨论更多的细节,为什么这样做:
重用app指导
。
我们我们在templates中创建一个admin目录,然后在Django源码中(django/contrib/admin/templates)默认的Django admin模板目录中复制admin/base_site.html到这个目录。
Django源码文件在哪里呢? 如果你找源码文件比较困难,可以运行下面的命令:
...\> py -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默认的管理页面都可以被重载。就像重载base_site.html页面一样。
自定义app的模板
狡猾的渲染器会问:如果DIRS默认是空的话,那么Django是如何查找默认的admin模板呢?答案就是,因为APP_DIRS设置为了True,Django自动的在每一个app包下寻找templates/子目录(不要忘记django.contrib.admin也是一个app)。
我们的投票app不是非常的复杂,不需要自定义admin模板。但是,如果它变得更加的精细,需要修改Django标准管理模板,修改app的模板更加明智,而不是修改项目中的模板。这样说的话,你可以包含投票app在任何新项目中,确保可以找到它所需要的自定义模板。
自定义管理员index页面
同样地,你可能像要自定义Django管理index页面的样式。
默认情况下,它会按照字母顺序显示所有在admin app注册过的且存在于INSTALLED_APPS中的app。你可能修改一下你的布局,总之,index是admin中最重要的页面,它应该容易使用。
像之前那样,自定义admin/index.html页面。编辑该文件,你就会看到它使用一个叫做app_list的模板变量。这个变量包含了Django中安装过的每一个app。你可以给它们一个固定的链接,连接到你认为合适的地方。
下一步
到这里,本系列就结束了。但是别忘了,之后的三个新系列。