开始Django之旅-part3_Django和view


上一篇:开始Django之旅-part2_Django和数据库mysql

下一篇:开始Django之旅-part4_使用表格简化你的代码

前言

这篇文章我们将要开始创建公共接口——view视图。

在Django中网页和其它内容都是由views传送过来的。每一个view都由一个Python函数表示。Django会通过检查请求的URL来选择合适的view。

现在在你的网页上,你可能已经能够理解像“ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B”这样的网址。你会更乐意了解Django提供给我们更加简洁的URL格式。

为了从URL到view,Django使用了所谓的URLconfs.一个URLconf将URL模式映射到views。

这篇文章提供了URLconfs基本的使用说明。more detail:URL dispatch。

开始编写视图views

在polls/views.py里边再写一些view,这些view稍稍有些不同,因为他们携带的参数不同。

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)

然后,要把这些view添加到你的路径(polls.urls)中去。

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/”,他们就会展示出占位符的结果和投票页面。

流程:当我们请求一个页面时(例如/polls/34/),Django就会加载mysite.urls,因为ROOT_URLCONF指向了它。它会找到名为urlpatterns的变量,然后按顺序匹配这个模式。在匹配polls/之后,它会剥离掉polls/,然后发送剩余的信息“34/”给polls.urls,然后就匹配到了“int:question_id/”,最后调用deltail()方法,把view视图展现出来了。

在这里插入图片描述
question_id=34就是来源于int:question_id。使用一个尖括号捕获了URL的一部分,并且作为一个参数传给了view函数。:question_id>定义了将要用于验证匹配模式的参数名,<int:是将匹配到的字符串转化成的类型。

这里不需要添加URL中像.html令人讨厌的东西,如果你想要添加的话,你可以这样做:

在这里插入图片描述
但是,别这样,显得太笨了。

写一些功能views

每个view都有责任要做一件事:要么就返回包含请求页面内容的HttpResponse对象,要么就显示一个像404的异常信息。剩下要做的由你做主。

你的view可以阅读数据库的记录。它可以使用了Django的系统模板或者一个第三方的模板系统。它可以在移动的过程中生成一个pdf文件,输入xml,生成zip文件或者你想要的东西。

所有的Django不可或缺的就是HttpResponse,或者一个异常。

这里有一个新的index view,它至少展示5条问题,使用逗号分开。

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系统模板,创建一个view可以使用的模板将界面和python代码分离。

首先,在polls目录下创建一个叫templates的目录,Django就会这templates下查找。

项目中TEMPLATE设置描述了Django如何加载和提供这些模板。配置文件配备了一个后端DjangoTemplates,它的APP_DIRS选项默认为true。按照惯例,DjangoTemplates会搜索INSTALL_APPS下的每一个app的模板目录。

在polls/templates目录下创建polls目录,在里边创件文件index.html。 换句话说你的模板位置为:polls/templates/polls/index.html. 你可以在html文件中引用模板。

模板命名空间:现在我们已经清楚的知道模板写在哪个位置了,但糟糕的是,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 %}

注意:为了让篇幅更短,所有的模板样例都不是完整的html代码,在你自己的项目中,应该使用完整的html代码。

现在更新polls/views.py 的index view:

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/),你就会看到无符号列表。链接到问题详情页面。

捷径:render()

加载模板是非常惯用的,填充内容,返回一个带有渲染结果的HttpResponse对象。Django提供了一个捷径,重写index()里的代码:

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。

render()函数将请求对象作为第一个参数,模板名作为第二个参数,数据作为第三个参数。它返回一个HttpResponse对象。

引发404错误

现在,让我们处理detail view的细节问题。这里有一些问题,看下面:

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不存在,detail就会引发一个Http404异常。

我们之后会讨论polls/detail.html模板放什么东西,但是现在你希望上面的例子能够运行,只需要这样:

polls/templates/polls/detail.html

{{ question }}

就可以解决了。

捷径:get_object_or_404()

如果对象不存在,使用get()调用Http404是一般的做法,Django提供了一个捷径,这里是detail() view,重写它:

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()函数采用model作为它的第一个参数,第二个参数是任意数字,它会调用model管理者的get()方法,如果对象不存在,就会调用Http404.

我们为什么要使用get_object_or_404()代替较高层次的自动捕获异常呢?因为那样model层和view层就结合在一起了,Django设计的一个重要目标是有较低的耦合性。一些受约束的耦合会在django.shortchuts模块介绍more

这里也有get_list_or_404()方法,它和get_object_or_404()的工作原理一样。除了使用filter()代替了get().如果list为空,则它就会调用Http404.

使用template系统

我们再回到detail()视图中来,考虑到内容变量question,这就是polls/detail.html模板实际的内容:

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

template系统使用点查找语法去访问变量的属性。 {{ question.question_text }}中,Django在字典中查找对象question,如果失败,它会尝试查找属性,查找正确就能执行,查找错了,就会尝试从列表中查询。

在 {% for %}循环中的question.choice_set.all表示python代码中的question.choice_set.all(),它会返回一个迭代的Choice对象,它适合在 {% for %}标签中使用。更多的template指导。

移除固定的代码

固定的代码,就是静态的代码,一般不会更改。

记住,当我们写指向polls/index.html模板的question的链接时,是这样写的:

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

如果我们要更改含有大量模板的项目中的URL,这就变得很困难,会产生一个强耦合问题。然而,因为你在polls.urls模块de path()方法中定义了name参数,你可以使用{% url %}模板标签移除某个在你url配置文件中已经定义好的URL路径上的依赖:

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

这种方法的工作方式是通过在polls.url模块中具体的URL定义。从下面你可以确切的看到命名为‘detail’的URL在哪里:

...
# the 'name' value as called by the {% url %} template tag
path('<int:question_id>/', views.detail, name='detail'),
...

如果你想去改polls detail view的URL到其它地方,比如polls/sepcifics/12/,你可以更改polls/url.py为:

...
# added the word 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
...

这样就不需要变动任何指向detail 视图的地址了。

url的命名空间

本项目仅仅是一个投票app,在真正的Django项目中,可能有5到20,甚至20以上个app,那么Django如何分辨他们里边的URl名称呢?比如,投票app有一个detail视图,也可能许多app在同一个项目中只是为了一个博客页面,那么,当使用{% url %}模板标签时,如何使得Django知道是哪个app视图会被创建了呢?

答案就是在你的URLconf中添加了命名空间。在polls/urls.py文件中,添加一个app_name属性,当做这个app的命名空间。

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>

上一篇:开始Django之旅-part2_Django和数据库mysql

下一篇:开始Django之旅-part4_使用表格简化你的代码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值