本教程从以下位置开始 Tutorial 2 停下来了。我们将继续使用网络投票应用程序,并将重点放在创建公共界面--“视图”上。
从何处获得帮助:
如果您在阅读本教程时遇到困难,请转到 Getting Help 常见问题部分。
视图是Django应用程序中的一种“类型”的网页,通常具有特定的功能和特定的模板。例如,在博客应用程序中,您可能有以下视图:
博客主页——显示最近的几条。
条目“详细信息”页面——单个条目的永久链接页面。
基于年份的存档页面——显示给定年份中包含条目的所有月份。
基于月份的存档页面——显示给定月份中包含条目的所有日期。
基于日期的存档页面——显示给定日期中的所有条目。
注释操作——处理对给定条目的发布注释。
在我们的投票应用程序中,我们将有以下四个视图:
问题“索引”页——显示最近几个问题。
问题“详细信息”页--显示问题文本,没有结果,但有一个要投票的表单。
问题“结果”页面——显示特定问题的结果。
投票行动——处理对特定问题中特定选择的投票。
在Django中,web页面和其他内容是通过视图传递的。每个视图都由一个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
. 这些观点略有不同,因为它们采取了一种论点:
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()
调用:
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。也可以尝试“/polols/34/results/”和“/polols/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模板系统——或者不使用。它可以生成一个PDF文件,输出XML,动态地创建一个zip文件,任何你想要的,使用任何你想要的python库。
Django想要的就是这个 HttpResponse
. 或者例外。
因为它很方便,让我们使用Django自己的数据库API,我们在 Tutorial 2 . 这是一次新的尝试 index()
视图,根据发布日期显示系统中最新的5个投票问题,用逗号分隔:
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
在每个目录中查找“templates”子目录 INSTALLED_APPS
.
内 templates
您刚刚创建的目录,创建另一个名为 polls
并在其中创建一个名为 index.html
. 换句话说,模板应该位于 polls/templates/polls/index.html
. 因为 app_directories
模板加载器的工作原理如上所述,您可以在Django中将此模板称为 polls/index.html
.
模板名称间距
现在我们 可以 直接将模板放入 polls/templates
(而不是创造另一个 polls
子目录),但这实际上是一个坏主意。Django将选择其找到的名称匹配的第一个模板,如果在 不同的 应用程序,Django将无法区分它们。我们需要能够将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 %}
备注
为了缩短教程,所有模板示例都使用不完整的HTML。在你自己的项目中,你应该使用 `complete HTML documents`_ _.
现在让我们更新我们的 index
视图 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/”加载页面,您将看到一个项目符号列表,其中包含来自的“最近发生了什么”问题 Tutorial 2 . 链接指向问题的详细信息页。
render()
¶加载模板、填充上下文并返回 HttpResponse
具有呈现模板的结果的对象。Django提供了一个快捷方式。这里是完整的 index()
视图,重写:
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
用给定上下文呈现的给定模板的对象。
现在,让我们来处理问题详细信息视图——显示给定投票的问题文本的页面。以下是视图:
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
稍后再使用模板,但如果您希望快速使上述示例正常工作,则文件仅包含:
{{ question }}
现在就开始吧。
get_object_or_404()
¶这是一个很常见的成语 get()
并提高 Http404
如果对象不存在。Django提供了一个快捷方式。这里是 detail()
视图,重写:
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
模板可能如下所示:
<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 有关模板的详细信息。
记住,当我们在 polls/index.html
模板,链接部分硬编码如下:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
这种硬编码、紧密耦合的方法的问题是,在具有大量模板的项目上更改URL变得非常困难。但是,由于您在 path()
中的函数 polls.urls
模块,您可以通过使用 {{% url %}}
模板标签:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
其工作方式是按照 polls.urls
模块。您可以看到下面定义“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"),
...
教程项目只有一个应用程序, polls
. 在真正的Django项目中,可能有五个、十个、二十个或更多的应用程序。Django如何区分URL名称?例如, polls
应用程序有一个 detail
在同一个项目上的应用程序,也可能是用于博客的应用程序。如何使Django知道在使用 {{% url %}}
模板标签?
答案是向URLConf添加名称空间。在 polls/urls.py
文件,继续添加 app_name
要设置应用程序命名空间:
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
模板:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
指向名称空间详细视图:
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
当你对写观点感到满意时,阅读 part 4 of this tutorial 了解有关窗体处理和常规视图的基础知识。
12月 18, 2023