Django教程之五-----编写你的第一个Django应用(3)

这个教程紧接(2).我们将继续讨论Web-poll应用并且将焦点放在创建公用接口--'views'上。

1. 概览

视图是一个在你Django应用里的网页页面类型,它通常提供特定的功能,并且有特定的模板。例如,在一个博客应用中,你应该有如下的视图:

  • 博客主页 -- 显示最新的条目。
  • 进入“详细”页面 -- 一个条目的固定链接
  • 基于年的档案页面 -- 显示给定年份的所有月份的条目
  • 基于月的档案页面 -- 显示给定月份的所有天数的条目
  • 基于天的档案页面 -- 显示给定天的所有条目。
  • 评论动作 -- 处理提交评论给一个指定条目
在我们的poll应用里,我们将有如下的视图:
  • Question “index” 页面 -- 显示最新的questions。
  • Question “detail” 页面 -- 显示一个question的文本,没有结果只有一个投票的表格。
  • Questino “result” 页面 -- 显示一个指定问题的结果。
  • 投票动作 -- 处理为一个指定问题的指定选项的投票。
在Django中,web页面和其他内容是由视图来提供的。每个视图都是由一个简单的Python函数来展示的(或方法,,在基于类的视图里)。Django将会通过检查所请求的URL来选择一个视图(准确的说,域名后面的URL部分)。

现在在你的网页上,你可能遇到这样美丽的东西“ME2/Sites/dirmode.asp?sid-&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B”。你将会很高兴的了解到,Django允许我们使用比那更优美的URL模式。

一个URL模式就是简单的URL大众形式 - 例如:/newsarchive/<year>/<month>/。

为了得到一个视图的URL,Django使用称为"URLconfs "的东西。一个URLconf映射URL模式(描述为常规表达式)到视图中。

这个教程提供了URLconfs使用的基础指导,你可以参考django.urls来寻找更多信息。

2. 编写更多的视图

现在让我们来添加更多的视图到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)
编写这些新的视图到polls.urls模型通过添加下列的url()调用:
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并且按顺序遍历正则表达式。当找到匹配“^polls/”的时候,它去除匹配的文本('polls/')并且将剩下的文本--'31/'--发送到'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模式是正则表达式,这里确实在你能用他们做什么上没有限制。并且也不需要添加URL的繁文缛节例如.html--除非你想这样,在这种情况下你可以这样做:
url(r'^polls/latest\.html$', views.index),
但是,不要那样做,它很蠢。

3. 编写一些实际上做事的视图

每个视图都响应一到两件事情:返回一个HttpResponse对象,包含被请求页面的内容,或者抛出一个诸如Http404的异常,这都取决于你。

你的视图能从数据库中读取记录,或者不能。它能够使用一个模板系统例如Django--或者一个3方Python模板系统--或者不能。他能生成一个PDF文件,输出XML,创建一个ZIP文件,任何你想要的事,使用你任何你想的Python库。

所有Django需要的就是HttpResponse。或者一个异常。

因为方便,让我们使用Django自己的数据库API,我们在教程(2)中提到过。下面是对一个新的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将会在哪里寻找模板。

你的项目的TEMPLATE设置描述了Django将会如何加载和渲染模板。默认的设置文件配置了一个DjangoTemplate后端,它的APP_DIRS选项被设置为True。通过约束,DjangoTemplates在每一个INSTALLED_APPS中寻找“templates”子目录。

在你刚刚创建的子目录里,创建另一个名为polls的目录,在那个目录中创建一个名为index.html的文件。换句话说,你的模板应该在polls/templates/polls/index.html。因为app_directiories模板加载器就如上述工作一般,你可以在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 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(你将会想要保留HttpResponse如果你仍然有detail,results,vote方法)。

render()函数将request对象作为它的第一个参数,模板名称作为它的第二个参数并且一个字典作为它的可选的第三个参数。它返回一个使用给定内容渲染的给定模板的HttpResponse对象。

4. 抛出404错误

现在,让我们处理问题明细视图 -- 显示给定调查问卷的问题文本的页面。下面是视图:
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是一个非常普遍的做法。下面是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异常,或者有抛出Http404而不是ObjectDoesNotExist的模型API?

因为那将会将模型层和视图层耦合。Django最重要的设计目标之一就是获取更少的耦合。django.shortcuts模板包含一些控制耦合的东西。

同样有get_list_or_404()方法,它用起来就像get_object_or_404() -- 除非使用filter()代替get()。如果列表是空,他将抛出Http404。

5. 使用模板系统

回到我们poll应用的detail()视图。给定内容变量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里面做一个字典查找。如果没有成功,它将会尝试一个属性查找 -- 在上面的例子中,它成功了。如果属性查找也失败了,它将会尝试一个列表索引查找。
发生在{% for %}循环中的方法调用:question.choice_set.all()被解释成Python代码question.choice_set.all(),它返回一个Choice对象的迭代器并且用在{% for %}标签中是合适的。

阅读 《模板指导》来获得更多的模板信息


6. 移除模板中的硬代码URLs

记住,当我们在polls/index.html模板中写到一个问题的链接时,链接部分是这样的硬编码:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
这种硬编码,紧耦合的方法的问题是,在一个有很多模板的项目中修改URLs将会是一个挑战。然后,因为你在polls.urls模型中的url()函数中定义了名称参数,你能够通过使用{% 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'),
...
如果你想要改变问卷调查明细视图的URL,如polls/specifics/12/而不是在模板中,你将会在polls/urls.py中修改:
...
# added the word 'specifics'
url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
...

7. 命名空间URL名称

教程项目有一个应用,polls。在一个实际的项目中,将会有5,10,20个应用或者更多。Django是如何分辨他们的URL名称呢?例如,polls应用有一个detail视图,也许同样是同样项目中博客应用的。当使用{% url %}模板标签时,Django是如何知道创建那一个应用的url呢?

答案是在你的URLconf中添加命名空间。在polls/ruls.py文件,继续田间一个app_name来设置应用的命名空间:
polls/urls.py
from django.conf.urls import url

from . import views

app_name = 'polls'
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
    url(r'^(?P<question_id>[0-9]+)/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>
改变为命名空间的明细视图:
polls/templates/polls/index.html
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值