自定义管理员表单
通过使用admin.site.register(Question)注册了Question模型之后,Django可以构造一个默认的表单。往往,你希望自定义管理员表单的外观和工作方式。在注册模型时,你可以告诉Django你想要的选项,以达到你的自定目的。
让我们通过重排表单上的字段来看看它是如何工作的。使用如下代码,替换admin.site.register(Question):
from django.contrib import admin
from .models import Question
class QuestionAdmin(admin.ModelAdmin):
fields = ['pub_date', 'question_text']
admin.site.register(Question, QuestionAdmin)
以上改变使得“Date published”在“Question Text”之前, 你可以通过改变fields列表中字段的次序,来改变UI元素的排列的先后顺序。
这只有两个控件,这并不令人印象深刻,但对于数十个控件的管理页面,选择一个直观的顺序是一个重要的可用性细节。
说到几十个控件的表格,你可能想把表格分成几个字段:
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date']}),
]
admin.site.register(Question, QuestionAdmin)
添加关联的对象
我们有了Question管理页面,但是一个Question有多个选项,而且管理页面没有显示选项。
有两个方法来解决这个问题。第一个方法是在admin中注册Choice,就如注册Question一样,这很简单:
from django.contrib import admin
from .models import Choice, Question
# ...
admin.site.register(Choice)
但是这样操作起来非常麻烦,将问题与选项独立起来了,也不符合逻辑。我们更希望在添加问题的时候,能够直接添加选项。
移除Choice模型的注册代码,并修改代码如下:
from django.contrib import admin
from .models import Question, Choice
# Register your models here.
class ChoiceInline(admin.StackedInline):
model = Choice
extra = 3
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date']}),
]
inlines = [ChoiceInline]
admin.site.register(Question, QuestionAdmin)
这段代码告诉Django:Choice对象在Question管理页面进行编辑。默认提供三个选项。你可以运行一下看看效果。
还有个小问题,我们可能会希望选项的UI元素都显示在同一行。你只需要修改ChoiceInline就行了:
class ChoiceInline(admin.TabularInline):
#...
为什么会多一个“Delete?”列呢?那是因为除了默认的三个选项,你可以通过系统提供的添加按钮,添加更多选项,而这些额外的选项,将会提供一个删除按钮,出现在“Delete?”列。
自定义修改页面
默认,Django会将每个对象的str()显示出来。但是有时候,我们还想显示一些其他的我们关心的字段。为此,使用list_display 管理选项,这是一个由字段名构成的元组,作为列显示:
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date', 'was_published_recently')
你可以点击列头来排序–除了was_published_recently之外。因为通过output of an arbitrary method排序是不支持的。还有就是,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?'
再次编辑polls/admin.py 文件, Question 修改 列表页作进一步的改进: 使用list_filter来进行数据过滤. 添加如下代码到QuestionAdmin:
class QuestionAdmin(admin.ModelAdmin):
...
list_filter = ['pub_date']
filter的类型取决于你设置的过滤字段 . 因为 pub_date是一个DateTimeField字段, Django 知道 给出哪些合适的过滤选项 : “Any date”, “Today”, “Past 7 days”, “This month”, “This year”.
我们再来添加搜索功能:
class QuestionAdmin(admin.ModelAdmin):
...
search_fields = ['question_text']
这将会在修改列表的顶部添加一个搜索框,并可以根据问题的文字进行搜索。你也可以设置多个搜索字段,但不宜过多。
是时候注意修改列表可以自由地分页。默认显示100个项每页。 修改列表的pagination, search boxes, filters, date-hierarchies, and column-header-ordering 协同工作,就如你所想那样。
自定义管理员界面的外观和感觉
管理员界面的很多内容是使用Django的模版系统生成的,我们必须做一些必要的修改和定制。
自定义项目模版
项目模版可以放在你的文件系统中任何Django可以访问到的地方。但是把它放在你的项目中是一个很好的约定。在你的项目目录(包含manage.py的目录)创建一个templates目录。打开设置文件(mysite/settings.py),然后在TEMPLATES设置中添加一个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模版时会检测它;它是一个搜索路径。
现在,在templates目录中创建一个admin子目录,然后,将Django默认的管理界面模版“admin/base_site.html ”(目录:django/contrib/admin/templates)拷贝到该admin目录下。
如何找到django目录?
方法1:
>>> sys.path
[...
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages']
方法2:
fwwdeair:~ fww$ python3 -c "import django;print(django.__path__)"
['/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django']
然后,编辑该文件,将 {{ 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 }}的内容. {% 和 {{ tags 是Django模版语言的内容。当Django渲染该模版文件时,模版语言会被转换为最终的HTML页面。
所有Django的默认模版都可以被覆盖。要覆盖一个模版,只需要将其从默认目录下拷贝到你自己的目录中,然后做出修改,就像你在base_site.html中所做的事情一样。
自定义应用模版
聪明的读者可能会问:如果“DIRS”是默认的空值,Django是如何寻找默认的管理模版的呢?答案是,因为”APP_DIRS“被设置为True,Django自动在每个应用包的
”templates/ subdirectory“中查找模版来使用,不要忘了,django.contrib.admin 也是一个应用。
我们的poll应用不是很复杂,并且不需要进行管理模版自定义。但是如果它变得复杂和功能强大,可能就需要修改标准的管理模版,而不是使用默认的。这样,你就可以将poll应用包含在任何项目中,但是你要确保它可以找到它所需要的自定义模版。
查看 “template loading documentation” 以获得更多关于Django 如何寻找其模版的内容。
自定义管理员索引页
同样,你可能也会想要自定义管理员索引页的外观。
默认的,它会按照字母顺序显示所有“INSTALLED_APPS”中的注册的应用。你可能想要更改其布局。毕竟,索引页是管理页面中最重要的,而且它应该易于使用。
自定义管理员索引页对应的模版是“admin/index.html”。(对它做相同的事情,就如你对admin/base_site.html所做的那样–把它从默认目录拷贝到你自己的目录)。
编辑该文件,你会看到它使用了一个模版变量叫做“app_list”。这个变量包含了每一个安装的Django应用。你可以强制连接到以对象形式描述的管理页面,来代替它。