Django — 视图是啥?模板是何物?
- 2020 年 1 月 20 日
- 筆記
前言
在Django的官方文档中是这么定义视图的: "一类具有相同功能和模板的网页的集合",概念比较抽象,我们直接 拿比较简单常见论坛网站来举例,可能要求创建以下视图:
- 论坛首页 — 列表页面,由时间先后倒叙展示新建的帖子
- 帖子详情 — 详细展示帖子内容
- 评论处理 — 为帖子增加评论的操作 在Django 中,系统所展示的页面和其他内容都是由视图派生而来,每一个视图可以表现为一个简单的Python函数,如果是基于类的视图的,则是对应的类中的一个方法。(以上内容也许现在难以理解,耐心看下去就会豁然开朗,^_^) Django 将会根据用户请求的URL来选择使用哪个视图。
在Django中,千万不要把视图理解为前端展示给我们的页面,请大家回顾下Django的MTV 模式。
编写视图
我们在第二篇公众号中已经演示了最简单视图,在 demo_app/views.py
中
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. This is your first Django")
根据前面的学习我们得知,当我们在浏览器中输入 http://localhost:8000/demo_app/
时,Django 的路由规则会将这个请求分配到这个 index
这个视图上进行处理,那如果我们输入的url带有参数(如带参数的Get请求)那我们的视图该如何处理呢?看看下面的实例
from django.http import HttpResponse * 不带参数的视图 def index(request): return HttpResponse("Hello, world. This is your first Django") * 带参数的视图 def hello_country(request, country_name): return HttpResponse("Hello," % country_name)
在 demo_app/views.py
中新增 hello_country
的配置新的路由规则:
from django.urls import path from . import views # . 表示在同级父目录下 urlpatterns = [ path('', views.index, name='index'), path('hello/<str:country_name>/', views.hello_country, name='hello'), ] str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式 int,匹配正整数,包含 0。 slug,匹配字母、数字以及横杠、下划线组成的字符串。 uuid,匹配格式化的 uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。 path,匹配任何非空字符串,包含了路径分隔符
当浏览器输入 http://localhost:8003/demo_app/hello/china
时,我们新配置的路由规则发挥作用,决定过了 demo_app/views.py
中的 hello_country
视图来处理该请求,那我们就会看到页面上返回

模板
在视图中你可以操作数据库,可以使用模板引擎(Django自带的,或者第三方的)等,Django 对视图的要求是返回一个 HttpResponse
,或者抛出异常。我们现在就来演示下Django 如何使用模板系统。
我们通过登录到后台管理系统新建几个Cat对象(也可通过Django自带的数据库API),我们将在视图中通过模板使用他们。
首先我们在 demo_app
目录下新建templates目录,Django将会在这个目录里查找模板文件。在Django的配置文件中默认设置了 APP_DIRS
为True,这一选项会让Django在每个 INSTALLED_APPS
文件夹中寻找 templates
子目录。
在刚新建的 templates
目录里再新建一个目录 demo_app
,然后在其中新建 index.html
文件。我们在这推荐使用这样的目录结构,虽然我们也可以个把 index.html
文件建在 demo_apptemplates
目录中。 index.html
新建后目录结构如下:
demo_proj/ manage.py demo_proj/ __init__.py settings.py urls.py wsgi.py demo_app migrations templates demo_app index.html __init__.py admin.py apps.py models.py test.py views.py
在 demo_app/tempaltes/demp_app/index.html
文件中输入如下代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Cat List</title> </head> <body> <table border="1"> <tr> <td>Name</td> <td>age</td> </tr> {% if cats_list %} {% for cat in cats_list %} <tr> <td>{{ cat.name }}</td> <td>{{ cat.age }}</td> </tr> {% endfor %} {% else %} <p>No cat</p> {% endif %} </table> </body> </html>
我们更新 demo_app/views.py
文件来调用我们刚才建立的模板
def index(request): cats_list = Cat.objects.all() from django.template import loader template = loader.get_template('demo_app/index.html') context = { 'cats_list': cats_list, } return HttpResponse(template.render(context, request))
上面代码的作用,就是载入 demo_app/index.html
模板文件,并向该模板传递一个context,这个上下文是个字典,可以理解为 将Python对象 cats_list
映射成 模板内的变量 cats_list
,所以,我们在模板中能够引用变量(类似于 {%ifcats_list%}
这样的格式{{ xxx }}).
用浏览器 访问 http://127.0.0.1:8003/demo_app/
,将会看到一个表格,列出了我们新建的几个Cat对象.

回顾下刚才views.py中使用模板的实现过程,我们先载入模板,再填充上下文,最后返回由它生成的 HttpResponse
对象,略显繁琐,Django提供了render() 函数来简化这个流程,我们重新更新 demo_app/views.py
文件
def index(request): cats_list = Cat.objects.all() context = { 'cats_list': cats_list, } return render(request,'demp_app/index.html',context)
重新用浏览器 访问 http://127.0.0.1:8003/demo_app/
,会返回同样的结果。
模板系统
回顾下我们的index()视图,它向模板传递了一个 cats_list
变量,我们再来看看 index.html
模板中是怎么使用变量的。
{% if cats_list %} {% for cat in cats_list %} <tr> <td>{{ cat.name }}</td> <td>{{ cat.age }}</td> </tr> {% endfor %} {% else %} <p>No cat</p> {% endif %}
在模板系统中,我们引用变量的属性都用 {{xx}}
, 如 {{cat.name}}
, {{cat.age}}
。 一些 判断,循环也有固定的格式:
- For 循环
{% for xx in xxx %} ... ... ... {% end for %}
- If 判断 (else 看实际情况)
{% if xxxxx %} {% else %} {% endif %}
- URL 路径 一般当列表页展示时,我们通常可以通过点击其中的一条记录进入到该记录的详情页(detail)。 这边我们假设程序中 detail 页面的视图,模板,路由配置都以配置好,当地址输入
http://xxxxx/demo_app/x
时进入id=x 的详情页面。其路由配置为
from django.urls import path from . import views # . 表示在同级父目录下 urlpatterns = [ path('', views.index, name='index'), path('hello/<str:country_name>/', views.hello_country, name='hello'), path('<int:id>/', views.detail, name='detail'), ]
我们将 index.html
改造下,让其显示id,并且id 为超链接
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Cat List</title> </head> <body> <table border="1"> <tr> <td>Id</td> <td>Name</td> <td>age</td> </tr> {% if cats_list %} {% for cat in cats_list %} <tr> <td><a href = "{% url 'demo_app:dtail' cat.id %}">{{ cat.id }}</a></td> <td>{{ cat.name }}</td> <td>{{ cat.age }}</td> </tr> {% endfor %} {% else %} <p>No cat</p> {% endif %} </table> </body> </html>
以前 href 中的网址 被 {%url'demo_app:detail'cat.id%}
所代替。注意其语法格式:
demo_app:detail
是指 在demo_app
应用的 urls.py 文件中name=detail
的路由规则。cat.id
则是传入的参数 以空格隔开。
模板系统当然还有其他的特定用法,我们会在后面的课程中陆续接触。
抛出异常
我们最后来处理下抛出异常的情况,那我们的detail视图来举例 在 demo_app/views.py
添加如下代码
from django.http import Http404 def detail(request, id): try: cat = Cat.objects.get(id = id) except Cat.DoesNotExist: raise Http404("Cat does not exist") return render(request, 'demo_app/detail.html',{'cat':cat})
根据id 获取具体的cat 对象,如果对象不存在,则抛出404异常 。否则,把获取的cat对象作为上下文传递给 detail.hmtl
。代码逻辑很清楚,但Django 还是提供了一个更为简单的函数 get_object_or_404()
from django.shortcuts import render,get_object_or_404() def detail(request, id): cat = get_object_or_404(Cat, id=id) return render(request, 'demo_app/detail.html',{'cat':cat})
get_object_or_404()
返回具体对象或者抛出404异常,类似的也有 get_list_or_404()
函数,大家可以直接练习下。