编写你的第一个 Django 应用程序,第 3 部分
本教程从教程 2结束的地方开始。我们将继续开展网络投票应用程序,并将重点放在创建公共界面——“视图”上。
从哪里获得帮助:
如果您在完成本教程时遇到问题,请转到常见问题解答的“获取帮助”部分。
概述¶
视图是 Django 应用程序中的一种网页“类型”,通常提供特定功能并具有特定模板。例如,在博客应用程序中,您可能有以下视图:
- 博客主页 – 显示最新的几个条目。
 - 条目“详细信息”页面 – 单个条目的永久链接页面。
 - 基于年份的存档页面 – 显示给定年份中包含条目的所有月份。
 - 基于月份的存档页面 – 显示给定月份中包含条目的所有日期。
 - 基于天的存档页面 – 显示给定日期的所有条目。
 - 评论操作 – 处理对给定条目发表评论。
 
在我们的民意调查应用程序中,我们将有 以下四个视图:
- 问题“索引”页面 – 显示最新的几个问题。
 - 问题“详细信息”页面 – 显示问题文本,没有结果,但带有投票表格。
 - 问题“结果”页面 – 显示特定问题的结果。
 - 投票操作 – 处理对特定问题中特定选择的投票。
 
在 Django 中,网页和其他内容是通过视图传递的。每个视图都由一个 Python 函数(或方法,如果是基于类的视图)表示。 Django 将通过检查请求的 URL(准确地说,是 URL 中域名后面的部分)来选择视图。
现在,在您上网的时候,您可能会遇到过这样的美女 ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B。您会很高兴知道 Django 为我们提供了 比这更优雅的URL 模式。
URL 模式是 URL 的一般形式 - 例如: /newsarchive/<year>/<month>/。
为了从 URL 获取视图,Django 使用所谓的“URLconfs”。 URLconf 将 URL 模式映射到视图。
本教程提供了 URLconfs 使用的基本说明,您可以参考URL 调度程序以获取更多信息。
写更多的观点¶
现在让我们向 中添加更多视图polls/views.py。这些观点略有不同,因为它们有一个论点:
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)
polls.urls通过添加以下 调用将这些新视图连接到模块中path():
polls/urls.py¶
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"),
]
在浏览器中查看“/polls/34/”。它将运行该detail() 函数并显示您在 URL 中提供的任何 ID。也尝试“/polls/34/results/”和“/polls/34/vote/”——它们将显示占位符结果和投票页面。
当有人从您的网站请求页面时 – 例如“/polls/34/”,Django 将加载mysite.urlsPython 模块,因为它是由设置指向的 ROOT_URLCONF。它找到名为的变量urlpatterns 并按顺序遍历模式。在找到匹配项后'polls/',它会去掉匹配文本 ( "polls/") 并将剩余文本发送 "34/"到 'polls.urls' URLconf 进行进一步处理。在那里它匹配'<int:question_id>/',导致对视图的调用,detail()如下所示:
detail(request=<HttpRequest object>, question_id=34)
该question_id=34部分来自<int:question_id>.使用尖括号“捕获”URL 的一部分并将其作为关键字参数发送给视图函数。字符串的这一question_id部分定义了用于标识匹配模式的名称,该int部分是一个转换器,用于确定哪些模式应与 URL 路径的这一部分相匹配。冒号 ( :) 分隔转换器和模式名称。
编写实际执行某些操 作的视图¶
每个视图负责执行以下两件事之一:返回 HttpResponse包含所请求页面内容的对象,或者引发异常,例如Http404.剩下的就取决于你了。
您的视图可以从数据库中读取记录,也可以不读取记录。它可以使用 Django 等模板系统,也可以使用第三方 Python 模板系统,也可以不使用。它可以生成 PDF 文件、输出 XML、动态创建 ZIP 文件,以及使用您想要的任何 Python 库。
姜戈想要的只是这样HttpResponse。或者是一个例外。
因为它很方便,所以我们使用 Django 自己的数据库 API,我们在教程 2中介绍过该 API 。这是一个新index() 视图,它显示系统中最新的 5 个民意调查问题,根据发布日期以逗号分隔:
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)
# Leave the rest of the views (detail, results, vote) unchanged
但这里有一个问题:页面的设计是硬编码在视图中的。如果您想更改页面的外观,则必须编辑此 Python 代码。因此,让我们使用 Django 的模板系统,通过创建视图可以使用的模板来将设计与 Python 分离。
首先,创建一个名为templatesin yourpolls目录的目录。 Django 将在那里寻找模板。
您的项目TEMPLATES设置描述了 Django 将如何加载和呈现模板。默认设置文件配置一个选项设置为 的 DjangoTemplates 后端。按照惯例,在每个.APP_DIRSTrue``DjangoTemplatesINSTALLED_APPS
在templates刚刚创建的目录中,创建另一个名为 的目录polls,并在其中创建一个名为 的文件 index.html。换句话说,您的模板应该位于 polls/templates/polls/index.html.由于app_directories 模板加载器的工作原理如上所述,您可以在 Django 中将此模板引用为polls/index.html.
模板命名空间
现在我们也许可以直接将模板放入 polls/templates(而不是创建另一个polls子目录),但这实际上是 一个坏主意。 Django 将选择它找到的第一个名称匹配的模板,如果您在不同的应用程序中具有相同名称的模板,Django 将无法区分它们。我们需要能够将 Django 指向正确的位置,确保这一点的最佳方法是为它们设置命名空间。也就是说,将这些模板放入以应用程序本身命名的另一个目录中。
将以下代码放入该模板中:
polls/templates/polls/index.html¶
{% 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 %}
笔记
为了使教程更简短,所有模板示例都使用不完整的 HTML。在您自己的项目中,您应该使用完整的 HTML 文档。
现在让我们更新index视图polls/views.py以使用模板:
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))
该代码加载调用的模板 polls/index.html并向其传递上下文。上下文是将模板变量名称映射到 Python 对象的字典。
通过将浏览器指向“/polls/”来加载页面,您应该会看到一个项目符号列表,其中包含教程 2中的“What's up”问题。该链接指向问题的详细信息页面。
快捷方式:¶render()
加载模板、填充上下文并返回 HttpResponse带有渲染模板结果的对象是一种非常常见的习惯用法。 Django 提供了一个快捷方式。这是重写后的完整index()视图:
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)
请注意,一旦我们在所有这些视图中完成此操作,我们就不再需要导入 loader和(如果您仍然拥有、 和 的存根方法,则HttpResponse需要保留)。HttpResponse``detail``results``vote
该render()函数将请求对象作为其第一个参数,模板名称作为其第二个参数,字典作为其可选的第三个参数。它返回HttpResponse 使用给定上下文呈现的给定模板的对象。
引发 404 错误¶
现在,让我们来处理问题详细信息视图 - 显示给定民意调查的问题文本的页面。这是视图:
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})
这里的新概念是:Http404如果具有请求的 ID 的问题不存在,则视图会引发异常。
polls/detail.html稍后我们将讨论您可以在该模板中添加哪些内容,但如果您想快速使上面的示例正常工作,则文件只包含:
polls/templates/polls/detail.html¶
{{ question }}
现在就让您开始吧。
快捷方式:¶get_object_or_404()
这是一个非常常见的习惯用法,如果对象不存在则使用get() 和引发。 Http404Django 提供了一个快捷方式。这是detail()重写后的视图:
polls/views.py¶
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})
该get_object_or_404()函数将 Django 模型作为其第一个参数和任意数量的关键字参数,并将其传递给get()模型管理器的函数。Http404如果该对象不存在,则会引发该异常。
哲学
为什么我们使用辅助函数get_object_or_404() 而不是 ObjectDoesNotExist在更高级别自动捕获异常,或者让模型 API raiseHttp404而不是 ObjectDoesNotExist?
因为 这会将模型层耦合到视图层。 Django 最重要的设计目标之一是保持松散耦合。模块中引入了一些受控耦合django.shortcuts。
还有一个get_list_or_404()函数,其工作原理与get_object_or_404()– 相同,只是使用 filter()代替 get()。Http404如果列表为空,则会引发该异常 。
使用模板系统¶
返回到detail()我们的民意调查应用程序的视图。给定上下文变量question,polls/detail.html模板可能如下所示:
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>
模板系统使用点查找语法来访问变量属性。在示例中,首先 Django 对对象进行字典查找。如果失败,它会尝试属性查找——在本例中这是有效的。如果属性查找失败,它会尝试列表索引查找。{{ question.question_text }}``question
方法调用发生在循环中: 被解释为 Python 代码 ,它返回对象 的可迭代对象,适合在标记中使用。{% for %}question.choice_set.all``question.choice_set.all()``Choice{% for %}
有关模板的更多信息,请参阅模板指南。
删除模板中的硬编码 URL ¶
请记住,当我们在模板中编写问题的链接时polls/index.html ,该链接是部分硬编码的,如下所示:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
这种硬编码、紧密耦合的方法的问题在于,更改具有大量模板的项目上的 URL 变得具有挑战性。但是,由于您在模块的函数name中定义了参数,因此您可以使用模板标签消除对 url 配置中定义的特定 URL 路径的依赖:path()polls.urls``{% url %}
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
其工作方式是查找 polls.urls模块中指定的 URL 定义。您可以准确地看到“detail”的 URL 名称定义如下:
...
# the 'name' value as called by the {% url %} template tag
path("<int:question_id>/", views.detail, name="detail"),
...
如果您想将投票详细信息视图的 URL 更改为其他内容,也许polls/specifics/12/可以更改为类似的内容,而不是在模板(或多个模板)中进行更改polls/urls.py:
...
# added the word 'specifics'
path("specifics/<int:question_id>/", views.detail, name="detail"),
...
命名空间 URL 名称¶
本教程项目只有一个应用程序,polls.在真正的 Django 项目中,可能有五个、十个、二十个或更多应用程序。 Django 如何区分它们之间的 URL 名称?例如,polls应用程序有一个detail 视图,同一博客项目上的应用程序也可能有一个视图。如何才能让 Django 在使用 模板标签时知道为某个 url 创建哪个应用程序视图?{% url %}
答案是将命名空间添加到 URLconf 中。在该polls/urls.py 文件中,继续添加app_name以设置应用程序命名空间:
polls/urls.py¶
from django.urls import path
from . import views
app_name = "polls"
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"),
]
现在将您的polls/index.html模板更改为:
polls/templates/polls/index.html¶
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
指向命名空间详细视图:
polls/templates/polls/index.html¶
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>