5. 视图概述

  • 视图即视图模块,接收web请求并返回web响应的事物处理模块

  • 响应指符合http协议要求的任何内容,包括json,string, html等

  • 是django后台的核心内容,一般在每个APP下的views.py文件里面,如果特别复杂可以有别的python文件

  • 视图可以看作一个接受HttpRequest的实例,返回一个Response的类的实例的类或者函数,形如:

      def views_func(request):
          ... ...
          return HttpResponse(...)
    
  • 相应的urls中对应的代码应该是:

      ... ...
      path("tulingxueyuan/", views.views_func)
      ... ...
    

5.1. 简单视图

  • 目的:

    • 创建一个简单视图函数
    • 返回一段html代码
    • 利用django.http包中的HttpResponse类
  • 分析:

    • HttpResponse是一个由django提供的视图类,可以用来返回简单视图
    • HttpRequest尽可能不让程序员改动内容,HttpResponse则需要手动改动内容
    • 为了代码简洁,从新创建v3_views项目,添加tuling应用
  • 操作步骤:

    1. 启动虚拟环境

      > source activate tuling_django
      
    2. 创建项目和app

       >django-admin startproject v3_views
       >cd v3_views
       >python manage.py startapp tuling
      
    3. 修改配置文件v3/settings.py

      每从创建一个APP都应该在settings.py下添加这个APP,否则可能会导致各种问题,简单应用 也可能不影响使用。

      • 添加app,修改完后代码如下:

              INSTALLED_APPS = [
                  'django.contrib.admin',
                  'django.contrib.auth',
                  'django.contrib.contenttypes',
                  'django.contrib.sessions',
                  'django.contrib.messages',
                  'django.contrib.staticfiles',
                  'tuling', #添加的新的APP
              ]
        
    4. 配置pycharm让软件能直接运行manage.py函数, 具体配置参看第一张django概述

    5. 运行 east/manage.py ,显示如下内容说明成功运行:

    6. 浏览器中输入 http://127.0.0.1:8000/, 确认链接成功

    7. 在 v3/urls.py 中添加url信息,代码如下:

       from django.urls import path, include #导入include
      
       urlpatterns = [
           path('admin/', admin.site.urls),
           # 新添加内容
           path('tuling/', include('tuling.urls')),
       ]
      
    8. 拷贝v3_views/urls.py到tuling文件夹下, tuling/urls.py 添加处理代码,代码如下:

       from django.contrib import admin
       from django.urls import path
       from . import views #导入视图
      
       urlpatterns = [
           path('t1/', views.t1),
       ]
      
    9. 在 tuling/views 添加处理代码,代码如下: ``` from django.shortcuts import render # 导入HttpResponse作为基本视图使用 from django.http import HttpResponse import datetime

       # Create your views here.
      
       def v1(request):
           # 得到系统当前事件
           now = datetime.datetime.now()
           # HttpResponse允许html代码作为返回值,
           html = "<html> <body> <h1> It is time {0}</h1> </body></html>".format(now)
      
           # 返回HttpResponse视图
           return HttpResponse(html)
      
       ```
      
    10. 运行以上代码并输入相应地址(http://127.0.0.1:8000/tuling/t1/)进行检查

    11. 简单视图创建完毕,需要正确理解HttpResponse视图类和访问流程

  • 数据流程 整个过程数据流程图大致如下:

    浏览器->django:v1/urls->tuling/urls->view.t1->HttpResponse->浏览器

5.2. 其他简单视图

  • django.http给我们提供类很多和HttpResponse类似的简单视图,通过查看django.http代码我们知道, 总共django给我们定义了一下一些简单类,如下图所示: 其他返回实例

  • 此类视图使用方法基本类似,可以通过return语句作为反馈直接反馈返回给浏览器

  • Http404为Exception子类,所以需要raise使用,代码案例如下

      # tuling/urls.py
      urlpatterns = [
          path('t1/', views.t1),
          path('t2/', views.t2), #引发异常
      ]
    
      # tuling/views.py
      .......
      from django.http import Http404
      .......
      def t2(r):
          """
          :param r: HttpRequest实例
          :return:
          """
          raise Http404("这个锅我不背,不背不背就不背")
    

    最终结果如下图所示:

    其他返回实例

5.3. HttpRequest对象

  • HttpRequest介绍

    • 服务器接收到http协议的请求后,会根据报文创建HttpRequest对象
    • 视图函数的第一个参数是HttpRequest对象
    • 在django.http模块中定义了HttpRequest对象的API
  • 属性

    • 下面除非特别说明,属性都是只读的

    • path:一个字符串,表示请求的页面的完整路径,不包含域名

        #加入请求的是以下博客地址
        http://www.mycode.wang/blog/liudana/124.html
        #则解析得到的request.path是:
        blog/liudana/124.html
      
    • method:一个字符串,表示请求使用的HTTP方法,常用值包括:’GET’、’POST’, 具体讲解会在Restfull中

    • encoding:一个字符串,表示提交的数据的编码方式

      • 如果为None则表示使用浏览器的默认设置,一般为utf-8
      • 这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值
    • GET:一个类似于字典的对象,包含get请求方式的所有参数

    • POST:一个类似于字典的对象,包含post请求方式的所有参数

    • FILES:一个类似于字典的对象,包含所有的上传文件

    • COOKIES:一个标准的Python字典,包含所有的cookie,键和值都为字符串

    • session:一个既可读又可写的类似于字典的对象,表示当前的会话,只有当Django 启用会话的支持时才可用,详细内容见“状态保持”

  • 方法

    • is_ajax():如果请求是通过XMLHttpRequest发起的,则返回True
  • QueryDict对象

    • 定义在django.http.QueryDict
    • request对象的属性GET、POST都是QueryDict类型的对象
    • 与python字典不同,QueryDict类型的对象用来处理同一个键带有多个值的情况
    • 方法get():根据键获取值
      • 只能获取键的一个值
      • 如果一个键同时拥有多个值,获取最后一个值
    • 方法getlist():根据键获取值
      • 将键的值以列表返回,可以获取一个键的多个值
  • GET属性

    • 当用户发起一个http协议中的get方法请求的时候,HttpRequest.GET会被各种值填充

    • 本质上是一个QueryDict类型的对象

    • 它包含get请求方式的所有参数

    • 一个常见的GET请求URL形如:

        HTTP:GET :  http://127.0.0.1:8080/a/b/c?id=1&k=w
      
    • 请求参数是与url请求地址中的参数对应,位于?后面的形如k=w的键值对

    • 参数的格式是键值对,如key1=value1

    • 多个参数之间,使用&连接,如key1=value1&key2=value2

    • 键是开发人员定下来的,值是可变的

    • 案例代码tuling/views/t3_get

        # tuling/urls.py
        ... ...
        path('t3/', views.t3_get),
        ... ...
      
        # tuling/v3_views.py下代码
        ... ...
        def t3_get(request):
            rst = ""
            for k,v in request.GET.items():
                rst += k + "-->" + v
                rst += ","
      
            return HttpResponse("Get value of Request is {0} ".format(rst))
      
    • 访问结果如图如图:

    访问结果和访问地址

    • 对于request.GET内容的使用,一定不能按正常字典的使用那样获取值,而应该使用 自带的get函数,确保不会出现问题,代码示例如下:

         #不应该这样使用,GET是一个特殊的字典
         s = r.GET["name"]
         #获取GET内值的方法应该是使用内置get方法
         r.GET.get("name", """)
      
  • POST属性

    • QueryDict类型的对象

    • 包含post请求方式的所有参数

    • 与form表单中的控件对应

    • 表单中空间必须有name属性,name为键,value为值

      • checkbox存在一键多值的问题
    • 键是开发人员定下来的,值是可变的

    • 案例 tuling/views/t4_post 和 tuling/views/t4_get

      • 因为需要用到模板,需要先设置下,后面会具体讲,settings中设置模板位置

          TEMPLATES = [
              {
                  'BACKEND': 'django.template.backends.django.DjangoTemplates',
                  #添加模板位置
                  'DIRS': [os.path.join(BASE_DIR, 'templates')],
                  ... ...
        
      • 设置get页面的urls和函数

          # tuling/urls.py
          urlpatterns = [
              ... ...
              #添加两个路由
              path('t4_get/', views.t4_get),
              path('t4_post/', views.t4_post),
          ]
        
      • views中设置代码

           # tuling/views.py
           # 在文件中添加下面两个处理函数
          def t4_get(r):
              return  render_to_response("for_post.html")
        
          def t4_post(r):
              rst = ""
              for k,v in request.POST.items():
                  rst += k + "-->" + v
                  rst += ","
        
              return HttpResponse("Get value of POST is {0} ".format(rst))
        
      • 添加文件和文件夹: /tuling/templates/for_post.html

      • 由于安全原因,需要在设置中安全选项中删除csrf设置

        # settings.py
          MIDDLEWARE = [
              ... ...
              'django.middleware.common.CommonMiddleware',
                #  下面这句话被注释掉
              #'django.middleware.csrf.CsrfViewMiddleware',
              'django.contrib.auth.middleware.AuthenticationMiddleware',
              ... ...1`
        

5.4. HttpResponse详解

  • 方法
    • init :使用页内容实例化HttpResponse对象
    • write(content):以文件的方式写
    • flush():以文件的方式输出缓存区
    • set_cookie(key, value=’’, max_age=None, expires=None):设置Cookie
      • key,value都是字符串类型
      • max_age是一个整数,表示在指定秒数后过期
      • expires是一个datetime或timedelta对象,会话将在这个指定的日期/时间过期,注意datetime和timedelta值只有在使用PickleSerializer时才可序列化
      • max_age与expires二选一
      • 如果不指定过期时间,则两个星期后过期
    • delete_cookie(key):删除指定的key的Cookie,如果key不存在则什么也不发生

5.5. 4. HttpResponseRedirect

  • 重定向,服务器端跳转

  • 构造函数的第一个参数用来指定重定向的地址

  • 案例 tuling/views.py

      # 在 tuling/urls中添加一下内容
       urlpatterns = [
           path('t1/', views.t1),
           path('t2/', views.t2),
           path('t3/', views.t3_get),
           path('t4_get/', views.t4_get),
           path('t4_post/', views.t4_post),
    
           path('t5_1/', views.t5_1),
           path('t5_2/', views.t5_2),
           path('t6/', views.t6, name="t6"),
    
    
    
       # tuling/views.py中添加以下内容
       # 导入下面内容
       from django.http import HttpResponse, Http404, HttpResponseRedirect
       from django.urls import reverse
    
       ... ...
    
       def t5_1(request):
           return HttpResponseRedirect("/tuling/t6")
    
       def t5_2(request):
           return HttpResponseRedirect(reverse("t6"))
    
       def t6(request):
           return HttpResponse("哈哈,这是t5的访问返回呀")
    

5.6. 手动编写视图

  • 实验目的:

    • 利用django快捷函数手动编写视图处理函数
    • 编写过程中理解视图运行原理
  • 分析:

    • django把所有请求信息封装入request
    • django通过urls模块把相应请求跟事件处理函数链接起来,并把request作为参数传入
    • 在相应的处理函数中,我们需要完成两部分
      • 处理业务
      • 把结果封装并返回,我们可以使用简单HttpResponse,同样也可以自己处理此功能,例如我们本例需要做的
    • 本案例不介绍业务处理,把目光集中在如何渲染结果并返回
  • render(request, template_name[, context][, context_instance][, content_type][, status][, current_app][, dirs][, using])

    • 使用模板和一个给定的上下文环境,返回一个渲染和的HttpResponse对象
    • request: django的传入请求
    • template_name: 模板名称
    • content_instance: 上下文环境
  • render_to_response(template_name[, context][, context_instance][, content_type][, status][, dirs][, using])[source]

    • 根据给定的上下文字典渲染给定模板,返回渲染后的HttpResponse
    • 已经不推荐
  • redirect(to, [permanent=False, ]*args, **kwargs)[source]¶

    • 参数可以是:

      1. 使用django.url.reverse的反向解析
      2. 绝对或者相对URL,将原封不动的作为重定向的位置
    • 默认返回一个临时的重定向, permanent=True时可以返回一个永久重定向

      所谓重定向就是将网页自动转向重定向,即:301永久性重定向和302临时性重定向。
      

5.7. 系统内建视图

  • 常用视图系统有内建,可以直接使用

  • 在http.views.defaults中定义

  • 404

    • default.page_not_found(request, template_name=’404.html’)
    • 系统引发Http404时出发
    • 默认船体request_path变量给模板,即导致错误的URL
    • DEBUG=True则不会调用404, 取而代之是调试信息
    • 404视图会被传递一个RequestContext对象并且可以访问模板上下文处理器提供的变量(MEDIA_URL等)
    • 参看示例代码:
  • 500(server error)

    • defaults.server_error(request, template_name=’500.html’)
    • 需要DEBUG=False,否则不调用
  • 403 (HTTP Forbidden) 视图

    • defaults.permission_denied(request, template_name=’403.html’)
    • 通过PermissionDenied触发
  • 400 (bad request) 视图

    • defaults.bad_request(request, template_name=’400.html’)
    • DEBUG=False
  • 示例代码

      # tuling/views.py
      # urls也需要进行设定
      from django.views import defaults
      ... ...
      def t7(r):
          return defaults.page_not_found(r, "找不到呀", "404.html")
    

5.8. 基于类的视图

到现在为止我们的视图都是一个一个函数,一般每个请求对应一个函数,本章开始学习利用面向对象来 编写视图。

  • 和基于函数的视图的优势和区别:

    • HTTP方法的methode可以有各自的方法,不需要使用条件分支来解决
    • 可以使用OOP技术(例如Mixin)
  • 概述

    • 核心是允许使用不同的实例方法来响应不同的HTTP请求方法,而避开条件分支实现

    • as_view函数作为类的可调用入口,该方法创建一个实例并调用dispatch方法, 按照请求方法对请求进行分发,如果该 方法没有定义,则引发HttpResponseNotAllowed

    • 参看案例1:

      • 修改tuling/urls.py

          path('t8/', views.TestView.as_view()),
        
      • 修改tuling/views.py

          from django.views.generic import View, TemplateView
        
          class TestView(View):
              def get(self, r):
                  return HttpResponse("Hello GET")
              def post(self, r):
                  return HttpResponse("Hello POST")
        
  • 类属性使用 每个类都可能有相应的类属性,我们基于类的视图一般都是作为视图类的子类存在,不可避免的需要一些 类的属性,此时一般类的属性的处理有两种方法:

    • 在类定义时直接覆盖

    • 在调用as_view的时候直接昨晚参数使用

    • TemplateView案例

      • 修改tuling/urls.py

          path('t9/', views.GreetingView.as_view()),
        
      • 修改tuling/views.py

          class GreetingView(TemplateView):
              #本类需要继承模板视图,默认有一个模板支持 
              template_name = "for_class_view.html"
        
              #getting 在urls里面给了一个赋值
              #调用post的时候明显看到getting的值发生了改变
              greeting = "Say something"
              def post(self, r):
                  data = dict(r.POST)
                  print(data)
                  print(self.greeting)
        
                  return  HttpResponse("Hello ")
        
  • 对基于类的视图的扩充大致有三种方法:

    • Mixin
    • 装饰as_view
    • 装饰dispatch
  • 使用Mixin

    • 多继承的一种形式,来自父类的行为和属性组合在一起
    • 解决了多继承问题
    • View的子类只能单继承,多继承会导致不可期问题
  • 在urls中装饰as_view

    # 此代码是django2.0 以前代码,不保证在django2上能继续运行
    from django.contrib.auth.decorators import login_required, permission_required
    from django.views.generic import TemplateView
    
    from .views import VoteView
    
    urlpatterns = [
        url(r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
        url(r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
    ]
    
  • 装饰类的dispatch函数

    • 类的方法和独立方法不同,不能直接运用装饰器,需要用methode_decorator进行装饰

      from django.contrib.auth.decorators import login_required
      from django.utils.decorators import method_decorator
      from django.views.generic import TemplateView
      
      class ProtectedView(TemplateView):
          template_name = 'secret.html'
      
          @method_decorator(login_required)
          def dispatch(self, *args, **kwargs):
              return super(ProtectedView, self).dispatch(*args, **kwargs)