定制化admin表单
通过使用admin.site.register(Question)注册Question模型,Django可以构造默认的表单。通常,可以通过对象的注册机制来告诉Django我们想要注册的选项,来定制化admin表单。
让我们通过重新排列表单的字段来看看它是如何工作的,打开polls/admin.py文件,使用如下代码替换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)
上述代码,创建一个admin模型类,将其作为第二个参数传递给admin.site.register()。这个特别的改变使得"Date published"字段位于"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的每个元组的第一个元素是fieldset的标题,下图所示是我们修改后的表单:
添加相关对象
现在,我们有了问题管理页面,但是每个问题有多个选项,并且管理页面没有显示这些选项。有两种方法解决这个问题,第一种方法在admin中注册Chice,就像操作Question一样,这个很容易。打开polls/admin.py文件,添加如下代码:
from .models import Question, Choice
admin.site.register(Choice)
现在在Django管理页面,就可以增加选项,表单如下图所示:
在该表单中,"Question"字段是一个下拉选择框,包含了数据中的每个question。在管理页面Django会把ForeignKey识别成一个下拉选择框。在我们的例子中,仅存在一个question。
当我们点击"Add Another"或者"+(加号)"时,我们会获得一个弹出框,上面显示的添加问题表单。如果在该窗口增加一个问题并且点击"保存(save)",Django会将该问题保存到数据库中,并且将其动态添加到"增加选项"选择框中,如下图所示:
但是,实际上这是一种低效率的方式在我们的系统中添加Choice,如果在我们创建Question时,可以批量添加选择项的话就会好很多。接下来我们就使用这种方式,打开polls/admin.py文件,编辑代码如下:
from django.contrib import admin
from .models import Question, Choice
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:在Question管理页面Choice对象被修改。默认情况下,为3个选项提供了足够的字段。加载"增加问题"页面将会看到如下图示:
在当前3个位置的底部,我们会看到"增加另一个Choice"链接。如果点击该链接,将会添加一个新的插槽;如果想删除添加的插槽,可以点击该插槽右侧的"X",该操作无法删除原始的3个插槽,如下截图所示:
然而,存在一个小问题。对于进入被关联的Choice对象,显示其所有字段会消耗很多的屏幕空间。因此Django提供了一种扁平化的对象显示,打开polls/admin.py文件,修改代码如下所示:
class ChoiceInline(admin.TabularInline):
model = Choice
extra = 3
使用TabularInline代替StackedInline,管理对象显示成紧凑的,表格化的格式,如下图所示:
注:表格有个额外的"删除"列,允许删除通过"增加另一个choice"按钮创建并且保存的行。
定制化改变列表
默认情况下,Django显示每个对象的str(),但是有的时候显示独立的字段可能对我们更有用。这样做,使用list_display管理选项,它是要显示字段名字的元组,作为对象的列显示在修改页面,打开polls/admin.py文件,修改代码如下所示:
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
]
inlines = [ChoiceInline]
list_display = ('question_text', 'pub_date')
为了更好的测试,我们也将was_published_recently()方法包含在该方法,如下代码:
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
]
inlines = [ChoiceInline]
list_display = ('question_text', 'pub_date', 'was_published_recently')
现在问题改变页面看起来如下入所示:
可以通过点击列头来对表进行排序,was_published_recently头是例外,因为排序不支持任意方法的输出。可以通过给该方法添加一些属性来改善该问题,打开polls/models.py文件,修改其代码如下:
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
def __str__(self):
return self.question_text
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。在QuestionAdmin中增加如下行:
list_filter = ['pub_date']
上述增加一个"过滤器"侧边栏,用户可以通过pub_date字段用过滤器来查找改变列表,如下图所示:
过滤器显示的类型依赖于你要过滤的类型。因为pub_date是一个DateTimeField,所以Django知道适配适合的过滤器选项:"任意日期"、"今天"、"过去7天"、"本月"、"今年"。让我们添加一些搜索内容,打开polls/admin.py文件,在QuestionAdmin中增加如下行:
search_fields = ['question_text']
页面显示如下如所示:
在修改列的上方增加了一个搜索框,当用户进入搜索项时,Django将会搜做question_text字段。你可以根据需要使用任意数量的字段,因为后台使用LIKE查询,限制搜索字段的可用数量对数据库做查询操作来时比较容易。
默认情况,每页显示100项,为了更好的页面显示,可以使用分页技术。通常情况下分页、搜索框、过滤器、日期层次和列头排序是综合使用来实现你想要的页面。
自定义管理外观
自定义项目模板:在项目目录(包含manage.py文件的目录)中创建一个templates目录,模板可以放在任何Django能够访问的目录,但是放在项目目录中是最方便的。打开项目配置文件: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',
],
},
},
]
当加载Django模板时,DIRS是要去检查的一系列文件目录,是一个搜索路径。在templates中创建一个名为admin的目录,将Django源代码(django/contrib/admin/templates)中默认的Django管理模板(admin/base_site.html)拷贝到刚才目录中去。
接下来,仅修改这个文件并且替换{{ 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渲染模板文件时,这些模板语言将会处理最终的HTML页面。
注:Django所有的默认管理模板都是可以重写的,重写模板就像我们之前修改base_site.html那样,从默认目录拷贝到自定义目录中,然后修改即可。
自定义应用模板:聪明的读者可能发现,默认情况DIR是空的,那么Django如何找到默认的管理模板呢?该问题的答案是:由于APP_DIRS是设置True,Django会在每个应用包里面查找templates/子目录,用作后备(不要忘记django.contrib.admin也是一个应用)。
模板文件加载详细地讲解Django如何查找模板。
自定义管理索引页面:默认情况,它显示的是INSTALLED_APPS中注册在管理应用中的所有应用,按字母表排序。
自定义的模板是admin/index.html。操作跟上部分的admin/base_site.html相同,编辑该文件,将会看到它使用了一个名叫app_list的模板变量,该变量包含了Django的每个installed应用。