1 概况
我们的投票应用需要下列几个视图:
- 问题索引页:展示最近几个投票的问题。
- 问题详情页:展示某个投票的问题和不带结果的选项列表。
- 问题结果页:展示某个投票的结果。
- 投票处理器:响应用户投票的操作。
2 编写更多视图
与官方教程的顺序不同,我们先填写 urls.py 。
# polls/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
用户访问的地址与 urlpatterns 里的条目成功匹配后,地址中的关键值将被捕获并赋给变量 question_id ,作为传入下列视图函数的参数。
# polls/views.py
# 问题详情页
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
# 问题结果页
def results(request, question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
# 投票处理器
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)
3 写一个真正有用的视图
每个视图必须要做的只有两件事:返回一个包含被请求页面内容的 HttpResponse 对象,或者抛出一个异常比如 Http404 。至于你还想干些什么,随便。
# polls/views.py
from django.http import HttpResponse
from .models import Question
# 问题索引页
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
output = ', '.join([q.question_text for q in latest_question_list])
return HttpResponse(output)
页面设计应当与视图代码分离。比如将问题索引页的模板文件放在 polls/templates/polls/index.html
。 虽然模板文件可以直接放在 polls/templates
文件夹中,但是这样不好。因为 Django 无法区分不同应用的同名模板文件,所以最好把它们放入各自的命名空间中,即和自身应用重名的子文件夹里。
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
然后更新问题索引页的视图。
# polls/views.py
from django.http import HttpResponse
from django.template import loader
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
context = {
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))
此时访问 http://localhost:8000/polls/ ,将会看到一个无序列表。
新视图函数做了三件事:载入模板、填充上下文、返回由它生成的 HttpResponse 对象。这一固定流程也可以通过快捷函数 render()
完成。
# polls/views.py
from django.shortcuts import render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
4 抛出 404 错误
将问题详情页的模板文件放在 polls/templates/polls/detail.html
,相关内容修改如下。
{{ question }}
# polls/views.py
from django.http import Http404
from django.shortcuts import render
from .models import Question
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, 'polls/detail.html', {'question': question})
如果 ID 所对应的问题不存在,抛出 Http404 异常。同样它也有快捷函数 get_object_or_404()
。 get_list_or_404()
和 get_object_or_404()
类似。
# polls/views.py
from django.shortcuts import render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
5 使用模板系统
polls/templates/polls/detail.html
不应如此简陋,让我们用模板语言丰富它。
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
6 去除模板中的硬编码 URL
polls/urls.py
中的 URL name 终于派上用场,结合标签 {% url %}
可以完美替代硬编码 URL 。 注意参数。
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
当你想修改 URL 时,不必修改模板,只要修改 polls/urls.py
就行。
7 为 URL 名称添加命名空间
from django.urls import path
from . import views
app_name = 'polls' # NEW!
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
上文的 {% url %}
也得指定命名空间。
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>