本教程从教程 2停止的地方开始。 我们正在继续进行Web轮询应用程序,并将重点放在创建公共接口 - “视图”上。
视图是Django应用程序中网页的“类型”,通常用于特定功能并具有特定模板。 例如,在博客应用程序中,您可能有以下视图:
在我们的投票应用程序中,我们将拥有以下四个视图:
在Django中,网页和其他内容由视图传递。 每个视图都由一个简单的Python函数(或基于类的视图的方法)表示。 Django将通过检查请求的URL(准确地说,域名后的URL部分)来选择一个视图。
现在在网络上你可能遇到过“ME2 / Sites / dirmod.asp?sid =&type = gen&mod = Core + Pages&gid = A6CD4967199A42D9B65B1B”等。 令人庆幸的是Django允许我们使用比这更优雅的URL模式。
URL正则只是URL的一般形式 - 例如:/newsarchive/[年]/[月]
为了从URL获得视图,Django使用了所谓的'URLconf'。 URLconf将URL模式映射到视图。
本教程提供了使用URLconf的基本说明,您可以参考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)
通过添加以下path()
调用,将这些新视图连接到polls.urls
模块中:
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
Python模块,因为它是由ROOT_URLCONF
设置指向的。 它找到名为urlpatterns
的变量(注:是列表)并按顺序遍历。 在'polls/'
找到匹配项后,它将匹配的文本(“polls/”
)剥离并发送剩余的文本 - “34/
到'polls.urls'URLconf以供进一步处理。 There it
matches '<int:question_id>/'
, resulting in a call to the detail()
view
like so:
detail(request=<HttpRequest object>, question_id=34)
question_id=34
部分来自<int:question_id>
. 使用尖括号“捕获” URL的一部分,并将其作为关键字参数发送给视图函数。 The :question_id>
part of the string defines the name that
will be used to identify the matched pattern, and the <int:
part is a
converter that determines what patterns should match this part of the URL path.
没有必要添加额外的URL后缀,如.html
- 除非你想,在这种情况下你可以这样做:
path('polls/latest.html', views.index),
But, don’t do that. It’s silly.
每个视图负责执行以下两项操作之一:返回包含请求页面内容的HttpResponse
对象,或引发诸如Http404
的异常。 The
rest is up to you.
您的视图可以从数据库中读取记录,或者不。 它可以使用Django等模板系统,也可以使用第三方Python模板系统。 它可以使用所需的任何Python库生成PDF文件,输出XML,即时创建ZIP文件。
Django想要的只是HttpResponse
。 或者是一个异常。
Because it’s convenient, let’s use Django’s own database API, which we covered
in 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分开。
首先,在polls
目录中创建一个名为templates
的目录。
Django将在那里寻找模板。
您项目的TEMPLATES
设置描述了Django如何加载和呈现模板。 默认设置文件配置DjangoTemplates
后端,其APP_DIRS
选项设置为True
。 按照惯例,DjangoTemplates
在每个INSTALLED_APPS
中寻找一个“templates”子目录。
在刚刚创建的templates
目录中,创建另一个名为polls
的目录,并在其中创建一个名为index.html
的文件。 换句话说,您的模板应位于polls / templates / polls / index.html
。 由于app_directories
模板加载器的工作方式如上所述,因此您可以在Django中将此模板简称为polls / index.html
。
模板命名空间
现在我们可能可以将模板直接放在polls / templates
中(而不是创建另一个polls
子目录),但是其实是个坏主意。 Django将选择找到的第一个名称匹配的模板,如果在不同应用程序中具有相同名称的模板,则Django将无法区分它们。 我们需要能够将Django指向正确的位置,而最简单的方法是通过namespacing来确保它们。 也就是说,将这些模板放在应用程序本身的另一个目录中。
将以下代码放入该模板:
{% 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
中更新index
视图以使用模板:
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))
That code loads the template called polls/index.html
and passes it a
context. The context is a dictionary mapping template variable names to Python
objects.
Load the page by pointing your browser at “/polls/”, and you should see a bulleted-list containing the “What’s up” question from Tutorial 2. The link points to the question’s detail page.
render()
¶It’s a very common idiom to load a template, fill a context and return an
HttpResponse
object with the result of the rendered
template. Django provides a shortcut. Here’s the full index()
view,
rewritten:
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
的方法)。
rrender()
函数将request对象作为第一个参数,将模板名称作为第二个参数,将字典作为可选的第三个参数。 它返回使用给定上下文呈现的给定模板的HttpResponse
对象。
现在,让我们解决问题详细信息视图-显示给定question text的页面。 这是视图内容:
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
异常。
稍后,我们将讨论您可以在polls / detail.html
模板中添加什么,但是,如果您想快速地使上述示例生效,则该文件仅包含:
{{ question }}
will get you started for now.
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()
函数。 It raises Http404
if the object doesn’t
exist.
Philosophy
为什么我们要使用一个辅助函数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 %}
标签。
有关模板的更多信息,请参见模板指南。
记得吗?在polls/index.html
模板中,我们连接到polls的链接是硬编码成这个样子的:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
问题出在硬编码,紧耦合使得在大量的模板中修改 URLs 成为富有挑战性的项目。 然而,因为你在polls.urls
模块的url()
函数中定义了name 参数,你可以通过使用{% url %}
模板标签来移除对你的URL配置中定义的特定的URL的依赖:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
它的工作方式是通过查找polls.urls
模块中指定的URL定义。 您可以在下面看到确切的网址名称“ detail”的定义:
...
# the 'name' value as called by the {% url %} template tag
path('<int:question_id>/', views.detail, name='detail'),
...
如果你想修改polls应用的detail视图的URL, 类似于polls/specifics/12/
的形式,你可以修改polls/urls.py
中的内容:
...
# added the word 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
...
该教程项目只有一个应用程序polls
。 In real Django projects,
there might be five, ten, twenty apps or more. Django如何区分它们之间的URL名称? 例如,polls
应用程序具有视图,在同一项目中用于博客的应用程序也可能具有detail
视图。 如何让Django知道在使用{% url %}
时为网址创建哪个应用视图模板标签?
答案是将命名空间添加到您的URLconf中。 In the polls/urls.py
file, go ahead and add an app_name
to set the application namespace:
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以了解简单表单处理和通用视图。
Jan 17, 2018