您的位置:首页 > 编程语言 > Go语言

啦啦啦啦~Django1.96---编写第一个Django应用(2)

2016-05-18 12:28 561 查看


理念


视图 是Django应用中的一“类”网页,它通常使用一个特定的函数提供服务,并且具有一个特定的模板。

例如,在博客应用中,可能有以下视图:

博客首页 —— 显示最新发表的博客。
博客“详细”页面 —— 单篇博客的固定链接页面。
基于年份的归档页面 —— 显示某给定年份里所有月份发表过的博客。
基于月份的归档页面 —— 显示在给定月份中发表过博客的所有日期。
基于日期的归档页面 —— 显示在给定日期中发表过的所有博客名称。
评论功能 —— 为一篇给定博客发表评论。

在我们的投票应用中,将有以下四个视图

Question首页 —— 显示最新发布的几个Question。
Question“详细”页面 —— 显示单个Question的具体内容,不显示该议题的当前投票结果,而是提供一个投票的表单。
Question“结果”页面 —— 显示特定的Question的投票结果。
投票功能 —— 处理对Question中Choice的投票。

在Django中,网页的页面和其他内容都是由视图来交付(视图对WEB请求进行回应)。

每个视图都是由一个简单的Python函数表示的(对于基于类的视图,一个视图是用一个方法来表示的)。

Django通过检查请求的URL来选择使用哪个视图。(准确地说,是URL里域名之后的那部分)

Django允许我们使用更加优雅的URL模式。

URL模式就是一个URL的通用形式 —— 例如: /newsarchive/<year>/<month>/。

Django使用叫做‘URLconfs’的配置来为URL匹配视图。

一个URLconf负责将URL模式匹配(使用正则表达式)到视图。


编写你的第一个视图

打开polls/views.py文件并将以下Python代码写入:

polls/views.py

from django.http import HttpResponse

def index(request):
return HttpResponse("Hello, world. You're at the polls index.")


这可能是Django中最简单的视图。

为了能够调用这个视图,我们需要将这个视图映射到URL上 —— 利用一个URLconf。

为了在投票应用目录内部创建URLconf,需要创建一个urls.py文件。你的应用的目录现在看起来应该像这样:

polls/
__init__.py
admin.py
models.py
tests.py
urls.py
views.py


在polls/urls.py文件中键入如下代码:

polls/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
url(r'^$', views.index, name='index'),
]


下一步,让主URLconf可以链接到polls.urls模块。在mysite/urls.py中插入一个include()

mysite/urls.py

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
url(r'^polls/', include('polls.urls')),
url(r'^admin/', include(admin.site.urls)),
]


现在我们已经将一个index视图关联到URLconf中。在你的浏览器中浏览 http://localhost:8000/polls/ ,
你会看到 “Hello, world. You’re at the polls index.”, 正如你在index 视图中定义的那样.

url()函数具有四个参数:两个必需的regex和 view,以及两个可选的kwargs和name。


url() 参数:regex

术语“regex”是“regular expression(正则表达式)”的常用的一个缩写,是一种用来匹配字符串中模式的语法,在这里是URL模式

Django从第一个正则表达式开始,依次将请求的URL与每个正则表达式进行匹配,直到找到匹配的那个为止。

请注意,这些正则表达式不会检索URL中GET和POST的参数以及域名。

例如,对于http://www.example.com/myapp/请求,URLconf
将查找myapp/。

对于http://www.example.com/myapp/?page=3请求,URLconf
也将查找myapp/。

最后,性能方面的一个注意点:这些正则表达式会在URLconf模块第一次载入的时候被编译。

它们超级快(只要这些正则表达式不像上面提醒的那样过于复杂)。


url() 参数:view

当Django找到一个匹配的正则表达式时,它就会调用view参数指定的视图函数

并将HttpRequest对象作为第一个参数,从正则表达式中“捕获”的其他值作为其他参数,传入到该视图函数中。

如果正则表达式使用简单的捕获方式,值将作为位置参数传递;

如果使用命名的捕获方式,值将作为关键字参数传递。


url() 参数:kwargs

任何关键字参数都可以以字典形式传递给目标视图。


url() 参数:name

命名你的URL。 这样就可以在Django的其它地方尤其是模板中,通过名称来明确地引用这个URL。

这个强大的特性可以使你仅仅修改一个文件就可以改变全局的URL模式。


编写更多的视图

现在让我们给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)


通过下面的url() 调用将这些新的视图和polls.urls模块关联起来:

polls/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
# ex: /polls/
url(r'^$', views.index, name='index'),
# ex: /polls/5/
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
# ex: /polls/5/results/
url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
# ex: /polls/5/vote/
url(r'^(?P<question_id>[0-9]+)/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 的变量并按顺序匹配其中的正则表达式

我们使用的include()函数来只是对其它URLconfs的简单引用。

注意include()函数的正则表达式的末尾没有$(字符串结束符号)而是一个斜线。

每当Django遇到 include()时,它会将URL中之前匹配到的字符串去掉,然后将剩下的字符串交由include()指定的URLconf
做进一步处理。

include()背后的想法是使URL变得即插即用。

因为投票应用拥有它自己的URLconf (polls/urls.py)

所以它可以被放在“/polls/”路径下,或者“/fun_polls/”路径下,或者“/content/polls/”路径下,或者其他别的什么路径下,该应用仍然能够工作。

以下是如果一个使用者访问“/polls/34/”,系统中将会发生的事:

Django发现匹配到了正则表达式'^polls/'

然后,Django将去掉匹配到的文本("polls/"))并将剩下的文本
—— "34/" —— 发送给‘polls.urls’ URLconf
做进一步处理,这时将匹配r'^(?P<question_id>[0-9]+)/$'并导致像下面这样调用detail()视图:

detail(request=<HttpRequest object>, question_id='34')


question_id='34'部分来自(?P<question_id>[0-9]+)。

使用圆括号包围一个模式可以“捕获”该模式匹配的文本并将它作为一个参数传递给视图函数

?P<question_id> 定义一个名字,它将用于标识匹配的模式

[0-9]+是匹配一串数字的正则表达式

因为URL模式是正则表达式,你如何使用它们没有什么限制。

不需要添加像.html这样繁琐的URL
—— 除非你执意这么做,在这种情况下你可以这样做:

url(r'^polls/latest\.html$', views.index),



编写拥有实际功能的视图

每个视图函数只负责处理两件事中的一件:

返回一个包含所请求页面内容的 HttpResponse对象;

抛出一个诸如Http404异常。

你的视图可以从数据库中读取记录,或者不读取数据库。

你可以使用一套模板系统,例如Django的 —— 或者第三方的Python模板系统 —— 或者不用模板系统。

你还可以动态地生成一个PDF文件、输出XML文件、创建一个ZIP文件或者使用你想用的Python 库生成任何想要的形式。

Django只要求返回的是一个HttpResponse。 或者抛出一个异常。

下面是一个新的index()视图,它显示系统中最新发布的5条questions记录,并用逗号分隔:

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([p.question_text for p 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将如何加载并渲染模板。

默认的设置文件settings.py配置了一个DjangoTemplates后端,其中将APP_DIRS选项设置为True。

按照惯例,DjangoTemplates在 INSTALLED_APPS所包含的每个应用的目录下查找名为"templates"子目录。

这就是为什么我们没有修改 DIRS,Django也能找到模板的原因。

组织模板

我们可以将我们所有的模板聚在一起,放在一个大的模板目录下,且可以运行地很好。

然而,我们的这个模板属于投票应用,不像我们在先前教程中创建的管理站点模板

我们将把它们放在应用的模板目录下(polls/templates)而不是项目模板目录下(templates)。

在你刚刚创建的templates目录中,创建另外一个目录polls,并在其中创建一个文件index.html。

换句话讲,你的模板应该位于polls/templates/polls/index.html。

由于app_directories 模板加载器按照上面描述的方式工作,在Django中你可以简单地用polls/index.html引用这个模板

模板命名空间

现在,我们可以直接将我们的模板放在polls/templates中(而不用创建另外一个polls子目录),但实际上这是个坏主意。Django将选择它找到的名字匹配的第一个模板文件,如果你在不同 的应用有相同名字的模板文件,Django将不能区分它们。我们需要将Django指向正确的模板,最简单的方式是使用命名空间。具体实现方式是,将这些模板文件放在以应用的名字来命名的另一个目录下

将以下的代码放入模板文件:

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 RequestContext, 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 = RequestContext(request, {
'latest_question_list': latest_question_list,
})
return HttpResponse(template.render(context))


以上的代码载入polls/index.html模板,并传给它一个context。


快捷方式:render()

常见的习惯是载入一个模板、填充一个context 然后返回一个含有模板渲染结果的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)


注意,一旦我们在所有的视图上都应用这个快捷函数

我们将不再需要导入 loaderRequestContextHttpResponse

(如果你没有改变先前的detail、results和 vote方法,你将需要在导入中保留HttpResponse)。

render()函数将请求对象作为它的第一个参数,模板的名字作为它的第二个参数,一个字典作为它可选的第三个参数。

它返回一个HttpResponse对象,含有用给定的context
渲染后的模板。



引发一个404错误

现在,让我们处理Question 详细页面的视图 —— 显示Question内容的页面: 下面是该视图:

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的Question,这个视图引发一个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最重要的一个设计目标就是保持松耦合。

还有一个get_list_or_404()函数,它的工作方式类似get_object_or_404()

—— 差别在于它使用filter()而不是get()。如果列表为空则引发Http404


使用模板

回到我们投票应用的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会接着尝试属性查询 —— 在这个例子中,属性查询会成功。

如果属性查询也失败,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模块的url()函数中定义了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
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
...


如果你想把polls应用中detail视图的URL改成其它样子比如 polls/specifics/12/,就可以不必在该模板(或者多个模板)中修改它,只需要修改 polls/urls.py:

...
# added the word 'specifics'
url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
...



带命名空间的URL名字

这个项目只有一个应用polls。在真实的Django项目中,可能会有五个、十个、二十个或者更多的应用。 Django如何区分它们URL的名字呢?

例如,polls 应用具有一个detail 视图,相同项目中的博客应用可能也有这样一个视图。

当使用模板标签{% url %}时,人们该如何做才能使得Django知道为一个URL创建哪个应用的视图?

答案是在你的主URLconf下添加命名空间。 在mysite/urls.py文件中,添加命名空间将它修改成:

mysite/urls.py

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
url(r'^polls/', include('polls.urls', namespace="polls")),
url(r'^admin/', include(admin.site.urls)),
]


现在将你的模板polls/index.html由:

polls/templates/polls/index.html

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>


修改为指向具有命名空间的详细视图:

polls/templates/polls/index.html

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: