django官方demo翻译简化版 三

概述

本章主要集中于django的view。view指django中的具有自己的模板及实现特定的功能的网页。比如说,一个博客里面可能包括以下这些views:

  • Blog homepage – displays the latest few entries.
  • Entry “detail” page – permalink page for a single entry.
  • Year-based archive page – displays all months with entries in the given year.
  • Month-based archive page – displays all days with entries in the given month.
  • Day-based archive page – displays all entries in the given day.
  • Comment action – handles posting comments to a given entry.

在polls项目里会有如下views:

  • Question “index” page – displays the latest few questions.
  • Question “detail” page – displays a question text, with no results but with a form to vote.
  • Question “results” page – displays results for a particular question.
  • Vote action – handles voting for a particular choice in a particular question.

在django里面网页及内容是通过views来进行传递的。每个view都代表了一个功能。django是通过查找url来匹配是找哪个view。

在实际浏览网页的过程中,你可能会遇到类似于“ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B”这样写的网页。然后django提供了比这种写法更好的url模型结构。

为了更好的编写view和url的关系,django使用了URLconfs这种结构。URLconfs指的就是描述view和url的映射关系。

本篇介绍了一些基本的URLconfs,如需探索更多,请移步官网的 URL dispatcher

构造简单的views模型

现在我们编写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文件

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'),
]

然后启动django应用,在浏览器访问/polls/34/,然后会调用detail() 方法并且会展示你在url里提供的任何id。然后调用“/polls/34/results/” and “/polls/34/vote/”也会调用对应的结果及投票页面。

当有人请求你的网站上的某个页面时-比如“/polls/34/”,django会加载mysite.urls 模块,因为这是在ROOT_URLCONF 里配置的。它会查找对应的urlpatterns并告知路由。在匹配到对应的url后,会去掉这个url,然后再根据后面的入参去做进一步的操作。也就是根据<int:question_id>/去调用detail()的view:

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

上面代码的question_id=34来源于<int:question_id>。尖括号里面的int:代表数据的类型,然后question_id代表入参。

实现demo的view

每个view都会实现1到2个目标:给一个包括了响应内容的HttpResponse 或者抛出一个404报错。这取决于你。

你的view可以取数据库的数据或者不取。可以使用django的模板系统或者第三方模板系统或者都不用。可以生成一个PDF文件,输出xml,创造一个zip文件或者使用任何python的依赖库

django需要的是一个HttpResponse或者一个异常。

现在开始编写我们的代码,使用数据库的数据,实现展示最新的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

上述代码有点问题:view中的网页设计是硬编码的。如果你需要修改网页的内容,你必须要修改python的代码。所以我们引入django的模板系统来做区分。

首先我们在polls的目录下创建一个templates的目录。django会在里面寻找对应的模板。

你的项目的TEMPLATES配置描述了django怎样加载和使用模板。默认配置项在TEMPLATES。为了方便DjangoTemplates会在每个安装的INSTALLED_APPS.下去查找模板子文件夹。

在刚才创建的templates目录下新建一个另外的文件夹polls,并在里面新建一个index.html文件。换句话说你的模板应该在polls/templates/polls/index.html。

然后在index.html中编写如下内容:

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文件如下:

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并且传递了一个字典内容给它。

快捷方式:render()

django提供了一个更方便的方式来实现上述逻辑,如下:

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 。

render()这个方法提供了三个入参,2个必传入参request及模板名称,以及一个可选的字典入参。它会返回一个html响应。

抛出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})

上述代码里出现了一个新的概念:这个view会抛出一个Http404 假如问题id对应的问题不存在。

我们会在晚点讨论在polls/detail.html应该写什么,但是如果你想早点让上述代码实现的话,可以按照如下这样写:

polls/templates/polls/detail.html

{{ question }}

便捷处理404

上述抛出404异常的,django提供了一个更快速的方式:

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的数据库model类,后续入参可以是任意的入参。假如不存在那么会抛出异常。存在会返回对应的信息。

使用模板系统

然后现在转回我们的detail这个view。应该这样编写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的字典查找,如果失败没有找到,那么会对这个question类进行属性值查找--也就是我们这种情况。如果属性查找也失败了,就会尝试进行索引查找。

{% for %} 循环中:question.choice_set.all被python解释成question.choice_set.all()。

移掉模板中的硬编码

记住,当我们在polls/index.html中给每个question增加一个链接时,这个链接会被像如下一样硬编码:

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

这种硬编码会在项目中出现很多模板时出现困扰,不知道对应的是哪个模板。但是可以通过path() 方法中定义的name属性来做区分:

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

在path中定义的如下:

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

如果你想修改polls detail view为polls/specifics/12/,那么你不需要修改模板,可以直接在polls/urls.py中修改:

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

给URL增加命名空间

这个项目只有一个app,但是真实的django应用中有很多个app。那么django怎么对他们做区分呢,假如有多个app中都包括了detail这个view?

这个答案是给URLconf增加命名空间。然后我们转到前面的文件polls/urls.py,在里面增加一行代码:

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>

改为现在的如下使用命名空间的模式:

polls/templates/polls/index.html


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

 

上一篇:Django的静态文件路径设置对比


下一篇:Django学习笔记 - gunxiaoshi