Django 3.0 文档笔记——视图与模板 tutorial 03

Django快速入门 03

https://docs.djangoproject.com/zh-hans/3.0/intro/tutorial03/

编写你的第一个 Django 应用,第 3 部分

在我们的投票应用中,我们需要下列几个视图:

问题索引页——展示最近的几个投票问题。
问题详情页——展示某个投票的问题和不带结果的选项列表。
问题结果页——展示某个投票的结果。
投票处理器——用于响应用户为某个问题的特定选项投票的操作。

Django使用了 URLconf(路由配置器)来将视图和URL连接起来,将 URL 模式映射到视图

编写更多视图

在之前我们编写了 view.py 中的 index 视图,并使之返回文字。下面我们按照上面的几个视图来进行添加

view.py

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.

def index(request):
    return HttpResponse("hello !")

def detail(request, question_id):
    return HttpResponse("question %s. " % question_id)

def results(request, question_id):
    return HttpResponse("results of %s. " % question_id)

def vote(request, question_id):
    return HttpResponse("voting of %s. " % question_id)

其中视图函数传入了 question_id 参数,以便在网页中返回 问题序号 

接着在 polls/urls.py 添加

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:q_id>/', views.detail, name='detail'),
    path('<int:q_id>/results/', views.results, name='results'),
    path('<int:q_id>/vote/', views.vote, name='vote'),
]

因为在外层的 urls.py 中已经include了 polls 所以不必重复添加(见第一部分)

当然,如果访问http://127.0.0.1:8000/polls/20/我们在 urls.py 中写的代码运行出来是错误的,报错 detail() got an unexpected keyword argument 'q_id'

我们来看一下URL与view之间的工作机制:

URL与view

我们访问了 polls/20/  因为 settings.py 文件中设置了

ROOT_URLCONF = 'my_try.urls'

所以先将  polls/20/  交给  my_try.urls.py  ,其中 polls 部分得到匹配,剪切到,剩下的  20/  转送到  polls.urls.py

在这里剩余文本匹配了(20符合int) '<int:q_id>/'

用尖括号捕获了20,便以 q_id = 20 形式调用 views.detail() ,但是 detail 并没有 q_id 参数,所以报错

所以我们看到,URL的参数名(尖括号) 应与 view函数的参数名保持一致

所以进行改进,如下:

from django.urls import path

from . import views

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

写一个真正有用的视图

每个视图必须要做的只有两件事:返回一个包含被请求页面内容的 HttpResponse 对象,或者抛出一个异常,比如 Http404

我们在 index() 函数里插入了一些新内容,让它能展示数据库里以发布日期排序的最近 5 个投票问题

from django.http import HttpResponse
from .models import Question

# Create your views here.

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)

这里注意 Question.objects 才能调用表格中的内容

同时 q.question_text  即question_text属性显示其文字内容

这里有个问题:页面的设计写死在视图函数的代码里的。
如果你想改变页面的样子,你需要编辑 Python 代码。
所以让我们使用 Django 的模板系统,
只要创建一个视图,就可以将页面的设计从代码中分离出来。

首先,在你的 polls 目录里创建一个 templates 目录。
Django 将会在这个目录里查找模板文件。

在 templates 中建立 polls 文件夹,在其中放置模板

编辑 polls/templates/polls/index.html

{% if latest_questions %}
    <ul>
    {% for question in latest_questions %}
        <li>
        <a href="/polls/{{ question.pk }}/">{{ question.question_text }}</a>
        </li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available. </p>
{% endif %}

更改 views.py

from django.http import HttpResponse
from .models import Question
from django.template import loader


# Create your views here.

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_questions': latest_question_list,
    }
    return HttpResponse(template.render(context))

上述代码,当我们调用 index() 时,通过 loader.get_template 调用了 'polls/index.html' 这个HTML文件,并通过html.render 带入上下文(context)。上下文是一个字典,键为HTML中的变量名,值为当前可调用的变量

在HTML文件中,当存在question时,遍历每一个question,并设置超链接,指向 polls/{{该问题id}}

一个快捷函数:render()

「载入模板,填充上下文,再返回由它生成的 HttpResponse 对象」是一个非常常用的操作流程。
于是 Django 提供了一个快捷函数,我们用它来重写 index() 视图:
from django.shortcuts import render
from django.http import HttpResponse
from .models import Question
from django.template import loader


# Create your views here.

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {
        'latest_questions': latest_question_list,
    }
    return render(request, 'polls/index.html', context)

render()函数的第一个参数是请求对象,第二个参数是模板名,第三个可选参数是字典。它返回使用给定上下文呈现的给定模板的HttpResponse对象。

抛出 404 错误

我们来处理投票详情视图——它会显示指定投票的问题标题

detail.html

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

views.py

from django.shortcuts import render
from django.http import HttpResponse, Http404
from .models import Question

def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except:
        raise Http404("not exist! ")
    return render(request, 'polls/detail.html', {'question': question})

向模板传递了上下文 question

在HTML中,显示question 的question_text的标题,并且在下面显示 choice的 choice_text

其中的 question.choice_set.all 的  choice_set  是一种Django方法 ,返回相关的objects,具体官方文档可参考如下链接

https://docs.djangoproject.com/en/dev/topics/db/queries/#related-objects

在 view 中,我们返回该 ID 下的question,如果无法返回,我们返回 404。同样使用 render

一个快捷函数:get_object_or_404()

尝试用 get() 函数获取一个对象,如果不存在就抛出 Http404 错误也是一个普遍的流程。
Django 也提供了一个快捷函数

view.py 更改 detail 函数

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, Http404
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() 而不是自己捕获 ObjectDoesNotExist 异常呢?
还有,为什么模型 API 不直接抛出 ObjectDoesNotExist 而是抛出 Http404 呢?

因为这样做会增加模型层和视图层的耦合性。
指导 Django 设计的最重要的思想之一就是要保证松散耦合。
一些受控的耦合将会被包含在 django.shortcuts 模块中。

去除模板中的硬编码 URL

还记得吗,我们在 polls/index.html 里编写投票链接时,链接是硬编码的:

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

问题在于,硬编码和强耦合的链接,对于一个包含很多应用的项目来说,修改起来是十分困难的。然而,因为你在 polls.urls 的 url() 函数中通过 name 参数为 URL 定义了名字,你可以使用 {% url %} 标签代替它:

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

我们之前在 urls 中为URL定义了名字

所以该 HTML编码会在 URL中寻找  'detail'  的一项,并将  id 赋给 question_id

这样,我们想更改URL时,只需要在 urls.py 文件里更改即可

为 URL 名称添加命名空间

教程项目只有一个应用,polls 。
在一个真实的 Django 项目中,可能会有五个,十个,二十个,甚至更多应用。
Django 如何分辨重名的 URL 呢?
举个例子,polls 应用有 detail 视图,可能另一个博客应用也有同名的视图。
Django 如何知道 {% url %} 标签到底对应哪一个应用的 URL 呢?

答案是:在根 URLconf 中添加命名空间。
在 polls/urls.py 文件中稍作修改,加上 app_name 设置命名空间:

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

在 index.html 中修改为:

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

指向了详细的 polls 的 detail

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值