Python Web开发-Django2.0学习04

本文介绍Django框架中的视图与模板的基本概念及应用技巧,包括视图函数定义、模板语法使用、异常处理、URL配置等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

四、视图和模板

1、使用视图

在我们的投票应用程序中,需要有四个视图,分别是:

  • 问题“索引”(index)页面 - 显示最新的几个问题。
  • 问题“详细”(detail)页面 - 显示一个问题文本,没有结果,但有一个表格投票。
  • 问题“结果”(results)页面 - 显示特定问题的结果。
  • 投票行动(vote) - 处理针对特定问题的特定选择的投票。

在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)

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)

接着修改URLConf,对polls/urls.py的代码进行修改:

from django.urls import path

from . import views

urlpatterns = [
    # ex: /polls/
    path('', views.index, name='index'),
    # ex: /polls/1/
    path('<int:question_id>/', views.detail, name='detail'),
    # ex: /polls/1/results/
    path('<int:question_id>/results/', views.results, name='results'),
    # ex: /polls/1/vote/
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

在polls/views.py中,我们直接生成一个字符串,作为http回复,返回给客户端。这一过程中使用了 django.http.HttpResponse()。

在这样的一种回复生成过程中,我们实际上将数据和视图格式混合了到上面的字符串中。看似方便,却为我们的管理带来困难。想像一个成熟的网站,其显示格式会有许多重复的地方。如果可以把数据和视图格式分离,就可以重复使用同一视图格式了。

Django中自带的模板系统,可以将视图格式分离出来,作为模板使用。这样,不但视图可以容易修改,程序也会显得美观大方。

2、使用模板

创建一个独立的index.html文件作为模板,路径为polls/templates/polls/index.html,文件夹templates和polls自己创建,内容如下:

{% 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,增加一个新的对象,用于向模板提交数据:

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))

3、使用shortcut:render()

Django提供了一个快捷方式来让我们加载模板,这里就需要使用render来替换之前使用的HttpResponse,这也是我们习惯性的使用方法。

重写polls/views.py中的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)

可以看到,使用render就不需要使用loader和HttpResponse,代码简洁了许多。render()函数将请求对象作为第一个参数,将模板名称作为第二个参数,并将字典作为其可选的第三个参数,这就是我们的数据。

4、404错误的处理

在从数据库获取数据时,存在数据不存在的情况,这时就需要对异常进行处理。在Python中我们的处理方式是使用类似try…except…的异常处理机制,在polls/views.py中,我们重写detail()方法,代码如下:

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

但实质上我们并不这样做,Django也给我们提供了一个快捷方式,重写polls/views.py中的detail()函数,代码如下:

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

detail.html里代码如下:
{{ question }}

get_object_or_404()函数将Django模型作为第一个参数,并将任意数量的关键字参数传递给模型管理器的get()函数。如果对象不存在,则引发Http404。

同样,也有一个get_list_or_404()函数,用于filter()而不是get()。如果列表为空,则引发Http404。

5、循环和选择

在polls/templates/polls/index.html中,可以看到,循环语句在{% for %}调用,通过{% endfor %}结束循环,选择语句在{% if %}调用,在{% endif %}结束。修改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>

PS.一般的变量之类的用 {{ }}(变量);功能类的,比如循环,条件判断是用 {% %}(标签)。

6、使用灵活的URL

在polls/templates/polls/index.html中,我们将链接硬编码为:

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

这样,URLConfig里面的URL改变了,这里链接也要改变。Django通过在模板使用URL name去URLConfig查找指定的URL定义,而不是直接“写死网址”。在polls/urls.py中可以看到:

path('<int:question_id>/', views.detail, name='detail'),

name可以用于在templates,models,views……中得到对应的网址,相当于“给网址取了个名字”,只要这个名字不变,网址变了也能通过名字获取到。在模板中通过{% url %}调用,改写polls/index.html,代码如下:

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

7、使用extends和include

网站模板的设计,一般的,我们做网站有一些通用的部分,比如导航、底部、访问统计代码等。可以写一个base.html来包含这些通用文件(include),然后用继承的方式来实现模板的复用。
先建立一个基础模板polls/templates/polls/base.html,代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>{% block title %}默认标题{% endblock %}</title>
</head>
<body>

{% include 'polls/nav.html' %}

{% block content %}
<div>这里是默认内容,所有继承自这个模板的,如果不覆盖就显示这里的默认内容。</div>
{% endblock %}

{% include 'polls/bottom.html' %}

</body>
</html>

polls/templates/polls/nav.html代码如下:

<p>这是导航栏</p>

polls/templates/polls/bottom.html代码如下:

<p>这是底部</p>

如果需要,写足够多的block以便继承的模板可以重写该部分,include是包含其它文件的内容,就是把一些网页共用的部分拿出来,重复利用,改动的时候也方便一些,还可以把广告代码放在一个单独的html中,改动也方便一些,在用到的地方include进去。其它的页面继承自base.html就好了,继承后的模板也可以在block块中include其它的模板文件。

比如我们的首页home.html,继承(extends)原来的base.html,可以简单这样写,重写部分代码(默认值的那一部分不用改),这样,我们可以使用base.html的主体,只替换掉特定的部分。polls/templates/polls/home.html代码如下:

{% extends "polls/base.html" %}

{% block title %}欢迎光临首页{% endblock %}

{% block content %}
{% include 'polls/ad.html' %}
这里是首页,欢迎光临
{% endblock %}

polls/templates/polls/ad.html代码如下:

<p>这是广告</p>

最后,自己试着定义视图函数和配置URL显示,代码如下:

#polls/views.py
def home(request):
    return render(request, 'polls/home.html')

#polls/urls.py
path('home/', views.home, name = 'home')

PS.模板一般放在app下的templates中,Django会自动去这个文件夹中找。但假如我们每个app的templates中都有一个index.html,当我们在views.py中使用的时候,直接写一个render(request, ‘index.html’),Django能不能找到当前app的templates文件夹中的index.html文件夹呢?(答案是不一定能,有可能找错)。

Django 模板查找机制:Django查找模板的过程是在每个app的templates文件夹中找(而不只是当前 app中的代码只在当前的app的templates 文件夹中找)。各个app的templates形成一个文件夹列表,Django 遍历这个列表,一个个文件夹进行查找,当在某一个文件夹找到的时候就停止,所有的都遍历完了还找不到指定的模板的时候就是Template Not Found(过程类似于Python找包)。这样设计有利当然也有弊,有利是的地方是一个app可以用另一个app的模板文件,弊是有可能会找错了。所以我们使用的时候在 templates 中建立一个app同名的文件夹,这样就好了。

这就需要把每个app中的templates文件夹中再建一个app的名称,仅和该app相关的模板放在app/templates/app/目录下面。


参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

森森向上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值