Django 学习笔记(四)—— 第一个自定义应用 下篇

本文接上篇 Django 学习笔记(三)—— 第一个自定义应用 中篇,涉及到代码重构(基于通用视图)和 Django 后台管理系统的定制。

一、通用视图

前面编写的 index()detail()results() 视图都很简单且存在一定的冗余问题。
它们都遵循 Web 开发中一种常见的模式,即根据 URL 中的参数从数据库中获取数据、载入模板文件然后返回渲染后的页面。
通用视图可以将这种常见的模式抽象化,使得在编写应用时能够减少大量不必要的代码。

修改视图定义文件(polls/views.py),借助通用视图改为如下版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.views import generic
from .models import Question, Choice

class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'

def get_queryset(self):
return Question.objects.order_by('-pub_date')[:5]


class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'


class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'


def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()

return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

ListViewDetailView 分别表示显示若干对象的列表显示一个特定类型对象的详细信息两种视图模板。
其中每个通用视图都可以用 model 属性指定其作用于哪个模型。
此外,DetailView 期望从 URL 中捕获名为 “pk” 的主键值,因此需要修改 polls/urls.py 中的具体定义。

视图对应的路由定义修改为如下形式(polls/urls.py):

1
2
3
4
5
6
7
8
9
10
from django.urls import path
from . import views

app_name = 'polls'
urlpatterns = [
path('',views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]

二、自定义管理后台

自定义后台表单

当前的 Django 后台管理界面如下:
Django 后台

投票问题

投票选项

对于投票问题(Questions)和选项(Choices)对象的编辑需要进入两个不同的页面分别进行修改,使用的友好性上有待提升。

可以使用如下方法,为 Questions 对象添加关联的 Choices 对象,即在投票问题的后台页面中添加上修改投票选项的接口,使得这两个页面合并为同一个。

编辑后台定义文件(polls/admin.py):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)

此时后台界面如下:
StackedInline

不过仍有点小问题,即修改对象时的后台页面占据了大量的屏幕区域用来显示 Choice 对象的字段。
Django 提供了一种表格的形式用来显示关联对象,只需作如下修改(polls/admin.py):

1
2
3
...
class ChoiceInline(admin.TabularInline):
...

效果如下:
TabularInline

自定义后台对象页面

当前后台管理系统中的对象主页(如 Question 对象的主页)布局如下:
Question

从该页面中无法直接看到除 question_text 之外的任何信息。下面将对该页面如下定制:

  • 显示更多的字段信息(如 pub_date)
  • 添加一个用来筛选数据的过滤器(基于日期)
  • 添加搜索功能(基于 question_text)

修改 polls/admin.py 文件,添加如下 3 行代码:

1
2
3
4
5
6
...
class QuestionAdmin(admin.ModelAdmin):
list_display = ('question_text', 'pub_date')
list_filter = ['pub_date']
search_fields = ['question_text']
...

效果如下:
过滤器和搜索

参考资料

Django 2.2 官方文档