Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第三部分(Page 8)
编写你的第一个 Django app,第三部分(Page 8)转载请注明链接地址
本页教程接前面的第二部分。我们继续开发 web-poll app,我们会专注于创建公共接口上 —— “视图”。
概述
在你的 Django app中,视图是一个 web 页面的类型,一般服务于一个特定的函数,并拥有一个特定的模板(template)。例如:在 blog app中,你可能有下面这些视图:
- Blog homepage – 显示少量最新的条目
- Entry “detail” page – 一个独立的永久链接入口
- Year-based archive page – 显示指定年份中所有月份的入口
- Month-based archive page – 显示指定月份所以天的的入口
- Day-based archive page – 显示指定天的所有入口
- Comment action – 处理给定条目提交的评论
在我们的 poll app中,我们会用到下面四个视图
- Question “index” page – 显示一些最新的问题
- Question “detail” page – 显示一个问题的内容,有一个用于投票的表单但没有结果
- Question “results” page – 显示特定问题的结果
- Vote action – 处理特定问题中特定选择的投票操作
在 Django 中,web 页面和其他内容被视图投递,每一个视图相当于一个简单的 Python 函数(或在基于类的视图中是方法)。Django 会检查被请求的 URL 来选择一个视图(确切的讲,是URL 中域名之后的部分)。
你在网络中可能遇到过类似“ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B”这样的URL。你会很高兴的知道 Django 允许比它更优雅的 URL 模式。
URL 模式只是 URL 的一般形式,—— 例如 /newsarchive/<year>/<month>/
.
从一个 URL 中获取一个视图,Django 使用我们学过的 “URLconf”。 URLconf 映射 URL 到视图。
本教程提供了使用 URLconfs 的基本指令,你可以在 URL dispatcher
(这里少一个链接) 中了解更多信息。
编写更多的视图
现在让我们在 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)
通过在 path()添加下面的调用,将这些新视图链接到 polls.urls
模块:
# 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/” —— 这会显示占位符的结果(占位符的结果:the placeholder results,没理解这个的意思)和投票页。
当有人在你的站点请求一个页面时 —— “/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 的一部分,并将其作为关键字参数发送费视图函数。字符串的部分:question_id>
用于定义名称,它会被用于识别匹配的模式;<int:
这一部分是一个转换器,它决定 URL 路径中的这一部分应该匹配什么模式。
不需要在 URL 中添加如“.html”一类的东西 —— 除非你想添加,这时你可以这样做:
path('polls/latest.html', views.index),
但是不要这样,这看起来太蠢了。
编写可以搞事情的视图
搞事情 == actually do something。
每一个视图负责做两件事情:返回一个包行请求页面内容的 HttpResponse(这里少一个链接) 对象, 或者 报告一个例如 Http404
的异常。剩下的就看你了。
你的视图可以从数据库中读取记录,或者不读。它可以使用一个模板系统,例如 Django 的或者第三方的 Python 模板系统,或者不使用。它可以使用你想用的 Python 库生成一个 PDF 文件、输出 XML、实时创建一个 ZIP 文件、任何你想创建的。
Django 想要的是一个 HttpResponse(这里少一个链接) 或者一个异常。
因为这样很方便,让我们使用我们在前一节提到的 Django 自己的数据库 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 中分离出来。
首先,在你的 polls
目录中创建一个名为 templates
的目录。Django 会从它里面查找模板。
你的项目的 TEMPLATES
(这里少一个链接)设置用来告诉 Django 如何加载和渲染模板。默认配置文件设置一个DjangoTemplates
后端,其 APP_DIRS
选项被设置为 True
。 按照惯例,DjangoTemplates
会在每个INSTALLED_APPS
(这里少一个链接)的子目录中查找模板。
在刚刚创建的 templates
目录中,创建另外一个叫 polls
的目录,并在里面创建 indexl.html
文件。换句话说,你的模板应该放在polls/templates/polls/index.html
。 因为 app_directories
模板加载器的工作方式和上面说的一样,你可以引用这个模板就像 django 引用polls/index.html
一样。
模板命名空间
现在我们可以直接将模板放入polls/templates
中(而不是创建另外一个polls
的子目录),但其实这并不是一个好主意。Django 会选择他找到的第一个名字匹配的模板,并且如果你的不同的app中有相同名字的模板,Django 会无法区分他们。我们需要 django 指向正确的那一个,最简单的方法就是通过命名空间来保证它正确。也就是说,将这些模板放入app自己命名的另外一个目录。
将下面的代码放入模板中:
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 %}
现在让我们用这个模板去更新polls/views.py
中的 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/”来加载页面,你会看到一个包含在前一节中创建的“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
这里少一个链接)(如果你没有修改之前的detail
、results
和 vote
方法,你将需要保留 HttpResponse
)
render()函数第一个参数是一个 request 对象,第二个参数是一个模板的名字,第三个参数是可选的,是一个字典。它返回一个使用给定上下文渲染模板后返回的 HttpResponse 对象。
引发一个404错误
现在我们来处理问题的 detail 视图 —— 显示给定投票的问题文本的页面。下面是视图的代码:
# 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})
这里有新的概念:如果问题 ID 不存在,那么视图会引发一个 Http404异常。
稍后我们会讨论你可以在 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
(这里少一个链接)
使用模板系统
返回我们 poll app中的 detail()
视。给定context 变量 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
对象上进行字典查询,如果失败,它会再尝试查找属性 —— 在这里的例子中,属性查找会成功,如果属性查找失败,Django会再尝试进行索引列表查询。
在 {% for %}(这里少一个链接)中发生的方法调用:question.choice_set.all
被解释成 python 代码中的 question.choice_set.all()
,他会返回一个可迭代的 Choice
对象,并且适用于{% for %}(这里少一个链接)标签。
移除模板中硬编码的 URLs
请牢记,在我们在polls/index.html
中编写一个question的链接的时候,链接的一部分是像下面一样硬编码的:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
硬编码的问题是,在项目中有许多模板要修改 URLs 的时候,会变得很困难。然而,你在 polls.urls
模块的 path()
函数中定义了 name 参数,这样你就可以通过使用{% url %}
模板标签,在你的 url 配置中移除对特定 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'),
...
如果你想要修改 polls中 detail 的 URL 修改成其他的样子,可能就像polls/specifics/12/
,你可以在 polls/urls.py
中完成修改,而不需要去修改模板:
...
# added the word 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
...
命名空间的 URL 名字
教程中的项目只有一个app——polls
。在实际的 django 项目中,可能会有5个、10个、20个或更多个。Django 是如何区分他们的 URL 名字的呢?例如 polls
app有一个 detail
视图,该项目下还有一个 blog app也有一个相同的视图。当使用{% url %}
标签时,如何让 Django 知道为 url 调用那个app的视图?
答案是在URLconf 中添加一个命名空间。在 polls/urls.py
文件中,添加一个app名来设置app的命名空间。
# 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>
修改成有命名空间的 detail 视图:
<!--polls/templates/polls/index.html-->
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
当你熟悉了编写视图,就可以继续学习下一节的内容了,学习简单的表单处理和通用视图。
Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第三部分(Page 8)的更多相关文章
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第一部分(Page 6)
编写你的第一个 Django app,第一部分(Page 6)转载请注明链接地址 Django 2.0.1 官方文档翻译: Django 2.0.1.dev20171223092829 documen ...
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第五部分(Page 10)
编写你的第一个 Django app,第五部分(Page 10)转载请注明链接地址 我们继续建设我们的 Web-poll 应用,本节我们会为它创建一些自动测试. 介绍自动测试 什么是自动测试 测试是简 ...
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第二部分(Page 7)
编写你的第一个 Django app,第二部分(Page 7)转载请注明链接地址 本教程上接前面的教程.我们会配置数据,创建你的第一个 model,并对Django 自动生成的 admin 站点进行快 ...
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第七部分(Page 12)
编写你的第一个 Django app,第七部分(Page 12)转载请注明链接地址 本节教程承接第六部分(page 11)的教程.我们继续开发 web-poll应用,并专注于自定义django的自动生 ...
- Django 2.0.1 官方文档翻译:编写你的第一个 Django app,第六部分(Page 11)
编写你的第一个 Django app,第六部分(Page 11)转载请注明链接地址 本教程上接前面第五部分的教程.我们构建了一个经过测试的 web-poll应用,现在我们会添加一个样式表和一张图片. ...
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第四部分(Page 9)
编写你的第一个 Django app,第四部分(Page 9)转载请注明链接地址 该教程上接前面的第三部分.我们会继续开发 web-poll 应用,并专注于简单的表单处理和简化代码. 写一个简单的表单 ...
- Django 2.0.1 官方文档翻译:编写你的第一个djang补丁(page 15)
编写你的第一个djang补丁(page 15) 介绍 有兴趣为社区做一些贡献?可能你发现了django中的一个你想修复的bug,或者你你想添加一个小小的功能. 回馈django就是解决你遇到的问题的最 ...
- Django 2.0.1 官方文档翻译: 高级教程:如何编写可重用的app (page 13)
高级教程:如何编写可重用的app (page 13) 本节教程上接第七部分(Page 12).我们会把我们的 web-poll应用转换成一个独立的python包,你可以在新的项目中重用或者把它分享给其 ...
- Django 2.0.1 官方文档翻译: 如何安装 django (Page 17)
如何安装 django(Page 17) 这一部分可以让你将 Django 运行起来. 安装 Python 作为 python 的一个 web 框架,Django 依赖 Python.Python 的 ...
随机推荐
- 怎样利用好单片机上的存储器资源来实现OD的存储与访问
转自:http://www.cnblogs.com/winshton/p/4897789.html 我们知道OD(对象字典)是CANopen的核心,所有功能都是围绕它开展的,是协议栈的数据中心,良好的 ...
- 深入理解JAVA集合系列三:HashMap的死循环解读
由于在公司项目中偶尔会遇到HashMap死循环造成CPU100%,重启后问题消失,隔一段时间又会反复出现.今天在这里来仔细剖析下多线程情况下HashMap所带来的问题: 1.多线程put操作后,get ...
- 【第二周】PSP
日期 C类别 C内容 S开始时间 E结束时间 I间隔(单位:分钟) T净时间(单位:分钟) 9月8日 编程 结对编程 12:15 13:15 10 50 编程 结对编程 16:35 17:30 ...
- 词频统计的java实现方法——第一次改进
需求概要 原需求 1.读取文件,文件内包可含英文字符,及常见标点,空格级换行符. 2.统计英文单词在本文件的出现次数 3.将统计结果排序 4.显示排序结果 新需求: 1.小文件输入. 为表明程序能跑 ...
- Delphi函数的out、var等关键字的作用,和使用场景
问题描述 Delphi函数的out.var等关键字的作用,和使用场景 Delphi函数的out.var等关键字的作用,和使用场景,我知道var是作为传值调用,但是像out这个关键字又是什么作用呢? 解 ...
- python基础(四)文件操作和集合
一.文件操作 对文件的操作分三步: 1.打开文件获取文件的句柄,句柄就理解为这个文件 2.通过文件句柄操作文件 3.关闭文件. 1.文件基本操作: f = open('file.txt','r') # ...
- C语言以字符形式读写文件
一.字符读取函数 fgetc (一).函数介绍 fgetc 是 file get char 的缩写,意思是从指定的文件中读取一个字符.函数原型为: int fgetc(FILE* fp) fp 为文件 ...
- Workstation和Virtualbox的虚拟机磁盘扩容方式.
1. 虚拟机磁盘管理, 更改磁盘格式是一个场景 还有一个场景是 硬盘空间不够了 需要扩充. 方法主要有两个. 如果是workstation的的虚拟机. 并且没有快照 可以直接GUI操作 如下图: 虚拟 ...
- yii2微博第三方登录
原作者:杜文建 原博客:http://www.cnblogs.com/dwj97/p/6530568.html yii2微博第三方登录 微博登录是最常用的第三方账号登录之一.由于其网站用户量大,可 ...
- docker-py安装
linux: pip install docker-py