Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第三部分(Page 8)

#编写你的第一个 Django app,第三部分(Page 8)转载请注明连接地址 本页教程接前面的第二部分。咱们继续开发 web-poll app,咱们会专一于建立公共接口上 —— “视图”。html

##概述 在你的 Django app中,视图是一个 web 页面的类型,通常服务于一个特定的函数,并拥有一个特定的模板(template)。例如:在 blog app中,你可能有下面这些视图:python

  • Blog homepage – 显示少许最新的条目
  • Entry “detail” page – 一个独立的永久连接入口
  • Year-based archive page – 显示指定年份中全部月份的入口
  • Month-based archive page – 显示指定月份因此天的的入口
  • Day-based archive page – 显示指定天的全部入口
  • Comment action – 处理给定条目提交的评论

在咱们的 poll app中,咱们会用到下面四个视图web

  • 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(这里少一个连接) 中了解更多信息。django

编写更多的视图

如今让咱们在 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”一类的东西 —— 除非你想添加,这时你能够这样作:app

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这里少一个连接)(若是你没有修改以前的detailresultsvote方法,你将须要保留 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 变量 questionpolls/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>

当你熟悉了编写视图,就能够继续学习下一节的内容了,学习简单的表单处理和通用视图。

相关文章
相关标签/搜索