Tutorial03

参考资料:Tutorial03

Overview

Django 中定义了一个特殊的对象,称作 view,它抽象地对应着一个网页,Django 通过一个函数来抽象表示一个 view。同时 Django 提供了叫做 URLconfs 的东西,会将一个个 URL 模式字符串一一对应到每一个 view 中。

Writing more view

我们可以在 polls/views.py 中加入以下代码:

1
2
3
4
5
6
7
8
9
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)

然后将以下代码添加到 polls/urls.py 文件中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from django.urls import path

from . import views

urlpatterns = [
    # ex: /polls/
    path('', views.index, name='index'),
    # ex: /polls/5/
    path('<int:question_id>/', views.detail, name='detail'),
    # ex: /polls/5/results/
    path('<int:question_id>/results/', views.results, name='results'),
    # ex: /polls/5/vote/
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

注意其中 <int:question_id> 这一部分:

  • <int: 这一部分将 URL 中的请求部分转化成指定的类型,同时决定指向的模式字符串。
  • :question_id> 这一部分给定匹配的模式字符串一个变量名。

PostScript:不建议愚蠢地将像 .html 一样的 URL cruft 添加到 path 中,比如:

1
path('polls/latest.html', views.index)

Write views that actually do something

每个 views 主要完成两件事情:返回包含所请求页面内容的 HttpRsponse 对象,或返回异常。

我们在 polls/views.py 中完成以下的事情(根据发布的日期显示系统中的最新的 5 个投票问题):

1
2
3
4
5
6
7
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)

这样的返回 Http 是硬核编码在 Python 代码中的,为了使前端代码与 Python 的后端代码分离,我们需要在应用中创建一个 templates 的子目录,进行渲染工作的 DjangoTemplates 会在每个 INSTALLED_APPS 中寻找一个 templates 的子目录。

所以我们创建目录以及文件 polls/templates/polls/index.html,并且写入以下内容:

1
2
3
4
5
6
7
8
9
{% 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,更改成以下内容:

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

主要到代码的最后一行:加载模板,Python 渲染,返回 Http 响应 → 是一个常用的功能,Django 将这三个功能集成到了一起形成了一个 shortcut,即可以用以下简化的代码表示:

1
2
3
4
5
6
7
8
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)

Raising a 404 error

Django 定义了一个返回 404 错误码的模块,通过以下方式导入:

1
from django.http import Http404

在应用中,我们使用抛出异常的方式使用导入的 Http404,如下:

1
raise Http404("This is a 404 error!")

一个实际的应用场景如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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})

这段代码的意思是,从 Question 对应的所有对象中寻找一个 pk=quesition_id 的对象,如果不存在则抛出 Http404 的异常。

这个功能比较常用,因此 Django 提供了一个 shortcut:get_object_or_404。一个实例代码如下:

1
2
3
4
5
6
7
from django.shortcuts import get_object_or_404, render

from .models import Question
# ...
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

还有一个类似的 shortcut 函数为:get_list_or_404

Removing hardcodeed URLs in templates

在之前编写的 html 模板文件中,href 指向的 url 出现了直接拼接字符串的硬编码方式,我们可以使用以下的方式替换这种写法:

1
2
3
4
5
<li><a href="{% url 'polls' question.id %}">{{ question.question_text }}</a></li>
<!--
原来的写法:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
--!>

这样做的好处是,将这个 hrefurlpatterns 中对应的模式字符串连接起来,他们的任何改变会相互影响。

Namespacing URL names

在实际的项目中可能存在许许多多的应用,那么使用上面的方法如何知道 URL 指向那个应用呢?

在应用的 urls.py 这个文件中,可以在 urlpattern 列表定义之前,加入:

1
app_name = 'polls'

于是,可以对将应用模板改成如下形式:

1
2
3
4
5
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
<!--
原来的写法
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
--!>