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()函数,大家可以直接练习下。


前文回顾