编写您的第一个Django应用程序,第3部分

本教程从以下位置开始 Tutorial 2 停下来了。我们将继续使用网络投票应用程序,并将重点放在创建公共界面--“视图”上。

从哪里获得帮助:

如果您在阅读本教程时遇到困难,请访问 Getting Help 部分的常见问题解答。

概述

视图是Django应用程序中的一种“类型”的网页,通常具有特定的功能和特定的模板。例如,在博客应用程序中,您可能有以下视图:

  • 博客主页--显示最新的几个条目。

  • 条目“详细信息”页面--单个条目的永久链接页面。

  • 基于年份的档案页面-显示给定年份中包含条目的所有月份。

  • 基于月份的档案页面--显示给定月份中包含条目的所有日期。

  • 基于日期的存档页面--显示给定日期的所有条目。

  • 评论操作--处理向给定条目发布评论。

在我们的民意调查申请中,我们将有以下四种观点:

  • 问题“索引”页面--显示最新的几个问题。

  • 问题“详细信息”页面--显示问题文本,没有结果,但带有投票表格。

  • 问题“结果”页面--显示特定问题的结果。

  • 投票动作--处理对特定问题中的特定选择进行投票。

在Django中,网页和其他内容是通过视图交付的。每个视图都由一个Python函数(或方法,对于基于类的视图)表示。Django将通过检查请求的URL(准确地说,是域名后面的URL部分)来选择视图。

现在,在你上网的时候,你可能遇到过这样的美女, ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B .你会很高兴知道Django让我们变得更加优雅 URL patterns 比这个。

URL模式是URL的一般形式-例如: /newsarchive/<year>/<month>/

为了从URL访问视图,Django使用所谓的“URLcon”。URLinf将URL模式映射到视图。

本教程提供了使用URLcon的基本说明,您可以参考 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.urls 模块,因为它由 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的一部分,并将其作为关键字参数发送给view函数。这个 question_id 字符串的一部分定义将用于标识匹配模式的名称,而 int Part是一个转换器,它确定哪些模式应该与URL路径的这一部分匹配。冒号 (: )分隔转换器和模式名称。

编写实际上可以做一些事情的视图

每个视图负责执行以下两件事之一:返回 HttpResponse 包含请求页面内容的对象,或引发异常,例如 Http404 .剩下的就看你了。

您的视图可以从数据库中读取记录,也可以不读取记录。它可以使用Django这样的模板系统或第三方Python模板系统,也可以不使用。它可以使用您想要的任何Python库生成PDF文件、输出ML、动态创建Zip文件、任何您想要的内容。

姜戈想要的只是 HttpResponse .或者是例外。

因为它很方便,让我们使用Django自己的数据库API,我们在中介绍了该API Tutorial 2 .这是一个新的尝试 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分开。

首先,创建一个名为 templates 在你的 polls 目录. Django会在那里寻找模板。

项目的 TEMPLATES 设置描述Django将如何加载和渲染模板。默认设置文件配置 DjangoTemplates 其后台 APP_DIRS 选项设置为 True .按照惯例 DjangoTemplates 在每个中查找“模板”收件箱 INSTALLED_APPS

templates 您刚刚创建的目录,创建另一个名为 polls ,并在其中创建一个名为 index.html .换句话说,您的模板应该位于 polls/templates/polls/index.html .因为 app_directories 模板加载器的工作原理如上所述,您可以在Django中将此模板称为 polls/index.html

模板命名空间

现在我们 might 能够直接将我们的模板放入 polls/templates (而不是创建另一个 polls ),但这实际上是一个坏主意。Django将选择它找到的第一个名称匹配的模板,并且如果您在 different 应用程序中,Django将无法区分它们。我们需要能够将Django指向正确的位置,确保这一点的最佳方法是 namespacing 他们也就是说,通过将这些模板放入其中 another 为应用程序本身命名的目录。

将以下代码放入该模板中:

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。在您自己的项目中您应该使用 complete HTML documents.

现在让我们更新我们的 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/”来加载页面,您应该会看到一个包含来自的“What ' s up”问题的项目符号列表 Tutorial 2 .该链接指向问题的详细信息页面。

捷径: 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)

请注意,一旦我们在所有这些视图中完成了此操作,我们就不再需要导入 loaderHttpResponse (you会想保留 HttpResponse 如果您仍然有 detailresults ,以及 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() 和提高 Http404 如果对象不存在。Django提供了一条捷径。是这样 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升高 Http404 而不是 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>

模板系统使用点查找语法来访问变量属性。的示例中 {{ question.question_text }} 首先Django对对象进行字典查找 question .失败后,它会尝试属性查找--在本例中,这是有效的。如果属性查找失败,它会尝试列表索引查找。

方法调用发生在 {% for %} 循环: question.choice_set.all 被解释为Python代码 question.choice_set.all() ,它返回的迭代对象 Choice 对象,适合在 {% for %} 标签。

请参阅 template guide 了解有关模板的更多信息。

删除模板中的硬编码URL

请记住,当我们在 polls/index.html 模板,链接部分硬编码如下:

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

这种硬编码、紧密耦合的方法的问题是,更改具有大量模板的项目上的URL变得具有挑战性。但是,由于您定义了 name 中的参数 path() 中的函数 polls.urls 模块,您可以通过使用 {% url %} 模板标签:

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

其工作方式是通过查找中指定的URL定义 polls.urls module.您可以准确地看到下面定义“详细信息”的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 %} 模板标签?

答案是将名称空间添加到您的URLinf中。在 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>

当您对撰写观点感到满意时,请阅读 part 4 of this tutorial 了解有关表单处理和通用视图的基础知识。