本文是在 python 的web框架 django 的入门教程 2 数据库 上开始的。根据 django 官方教程 学习而来,内容也与其教程3配对。
下一章:python 的web框架 django 的入门教程 4 表单
继续网上投票程序,这章关注的是建立接口View。
view在django 中是网页的一种类型,他有特定的功能和模板。
在我们的polls应用程序中,我们有以下四个视图:
Question“index”页面–显示最新的几个问题。
Question“detail”页面–显示问题文本,没有结果,但有一个投票表。
Question“result”页面–显示特定问题的结果。
polls行动–处理特定问题中特定选择的投票。
为了从URL到view,Django使用了“ URLconfs”。 URLconf将URL映射到view。
添加更多view
给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模块需要给这些view建立关联,修改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 ,也就是http://127.0.0.1:8000/polls/34/时,显示:
You're looking at question 34.
http://127.0.0.1:8000/polls/34/vote/ 时,显示 You're voting on question 34.
http://127.0.0.1:8000/polls/34/results/ 时,显示 You're looking at the results of question 34.
原文教程这么说,但我开始显示404 错误。查看mysite\mysite\urls.py 文件。
path('polls/', include('polls.urls')), 这一行被注释了,取消注释,才得到上面的结果。
过程说明
当请求网页比如:“/polls/34/” 的时候,首先到mysite\mysite\urls.py , 因为这里是ROOT_URLCONF,遍历变量
urlpatterns,
/polls对应/polls,这样找到polls/urls.py,并把剩余参数34/传递给他。在polls/urls.py里遍历变量urlpatterns,匹配<int:question_id>/的就是views.detail,最后调用polls/views.py里的 detail()如下:
detail(request=<HttpRequest object>, question_id=34)
question_id=34
来自 <int:question_id>
,int: 转化为int。
编写实际内容的view
每个view执行以下2项操作之一:返回包含所请求页面内容的HttpResponse对象,或引发诸如Http404之类的异常。 其余内容的取决于你的设计。
view可以从数据库中读取记录。 可以使用Django或者第三方Python模板。 可以使用所需的任何Python库生成PDF文件,输出XML,即时创建ZIP文件。
Django想要的只是HttpResponse或者一个异常响应。
我们这里使用Django自己的数据库API,在教程2中有了介绍。下面是index()的新view,它根据发布日期显示系统中最新的5个polls问题,并用逗号分隔。
修改前面的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
保存,运行后我们只是显示一些文字。要有格式等需要编辑py文件很复杂。这里就引入模板,让形式和内容分离。
polls目录下建立一个目录 templates,然后templates下再建一个polls目录,最后文件是这样的路径: polls/templates/polls/index.html 。
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 %}
更改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))
现在运行测试有格式了。
快捷函数 render()
加载模板,填充上下文并返回HttpResponse对象,这些操作太常见了,所有有个快捷函数render(),上面代码更新为:
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,不过我们这里暂时需要,因为其他view 还用了他。
引发404错误
现在转到detail 这个view ,有错误时引发404 错误。
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})
快捷函数: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_list_or_404(),如果list 为空的时候,引发404。
对应detail()的模板文件 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%中使用 } 标签。
删除模板中的硬编码URL
当我们在polls / index.html模板中编写到question的链接时,该链接有部分硬编码,如下所示:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
这种硬编码的问题在于,在有大量模板的项目上更改URL变得具有挑战性。 由于在polls.urls模块的path()函数中定义了名称参数,因此可以使用{%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'),
...
name 的值 detail 在{% url %} 中被替换
如果将polls detail 的URL更改为其他内容,例如,将其更改为polls/ specifics / 12 /之类的内容,而不是在一个或多个模板中进行更改,则可以在polls/urls.py中进行更改:
...
# 添加单词 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
...
命名空间URL名称
教程项目只有一个应用程序,polls。 在实际的Django项目中,可能有五个,十个,二十个或更多应用程序。 Django如何区分它们之间的URL名称? 例如,polls应用程序具有详细信息视图,同一项目中的应用程序也可能具有博客视图。 如何做到这一点,以便Django在使用{%url%}模板标记时知道为该URL创建哪个应用视图?
答案是将名称空间添加到您的URLconf中。 在polls / urls.py文件中,添加一个app_name来设置应用程序名称空间:
app_name = 'polls'
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 模板文件修改为:
从
<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>
原来的detail 变为 polls:detail了。
本章内容就到此为止。如果对view 比较有感觉了,可以进入下一章:python 的web框架 django 的入门教程 4 表单