Django2.X—FBV视图(全面)

视图是django的MTV架构模式的V部分,主要负责处理用户请求和生成响应内容,然后在页面或其他类型文档中显示。也可以理解为视图是MVC架构里面的C部分,主要处理功能和业务上的逻辑,我们习惯使用视图函数处理HTTP请求,即在视图定义def函数,这种方式称为FBV(Function Base Views )。

1、设置响应方式

1.1 返回响应内容

视图函数是通过return方式返回,然后生成相应的网页内容呈现在浏览器上,return是Python的内置语法,用于设置函数的返回值,若要设置不同的响应方式,则需要使用Django内置的响应类。如图:

响应类型 说明
HttpResponse(‘Hello world’) 状态码200,请求已成功被服务器接受
HttpResponseRedirect(’/’) 状态码302,重定向首页地址
HttpResponseBadRequest(‘400’) 状态码301,永久重定向首页地址
HttpResponseNotFound(‘404’) 状态码404,网页不存在或网页的URL失效
HttpResponseForbidden(‘403’) 状态码403,没有访问权限
HttpResponseNotAllowed(‘405’) 状态码405,不允许使用该请求方式
HttpResponse(‘500’) 状态码500,服务器内容错误
JsonResponse({‘foo’:‘bar’}) 默认状态码200,响应内容为JSON数据
StreamingHttpResponse() 默认状态码200,响应内容以流式输出

不同的响应方式代表不同的HTTP状态码,其核心作用是Web Server服务器用来告诉浏览器当前网页发生了什么事,或者当前Web服务器的响应状态。上述的响应类主要来自于模块django.http,该模块是实现响应功能的核心。以HttpResponse为例,在MyDjango项目的index文件夹的urls.py和views.py中编写功能代码:

# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
    # 定义首页的路由
    path('', views.index, name='index'),
]

# index的views.py
from django.http import HttpResponse
from django.shortcuts import render

def index(request):
    html = '<h1>Hello World</h1>'
    return HttpResponse(html, status=200)

视图函数index使用响应类HttpResponse实现响应过程。从HttpResponse的参数可知,第一个参数是响应内容,一般是网页内容或JSON数据,网页内容是以HTML语言为主的,JSON数据用于生成API接口数据。第二个参数用于设置HTTP状态码,它支持HTTP所有的状态码。
从HttpResponse的使用过程可知,如果生成网页内容,就需要将HTML语言以字符串的形式表示,如果网页内容过大,就会增加视图函数的代码量,同时也没有体现模板的作用。因此,Django在此基础上进行了封装处理,定义了函数render、render_to_response等。
render和render_to_response实现的功能是一致的。render_to_response自2.0版本以来已开始被弃用,但并不代表在2.0以上版本无法使用,只是大部分开发者都使用render。对render进行说明,语法如下:

render(request,template_name,context=None,content_type=None,status=None,using=None)

render的参数request和template_name是必须参数,其余参数都是可选参数,各个参数如下:

  • request: 浏览器向服务器发送的请求对象,包含用户信息、请求内容和请求方式等。
  • template_name: 设置模板文件名,用于生成网页内容。
  • context: 对模板上下文(模板变量)赋值,以字典个格式表示,默认情况下是一个空字典。
  • content_type: 响应内容的数据格式,一般情况下使用默认值即可。
  • status: HTTP状态码,默认为200。
  • using: 设置模板引擎,用于解析模板文件,生成网页内容。

1.2 设置重定向

重定向的状态码分为301和302,前者时永久性跳转,后者是临时性跳转的,两者的区别在于搜索引擎的网页抓取。301重定向是永久的重定向,搜索引擎在抓取新内容的同时会将旧的网址替换为重定向之后的网址。302跳转时暂时的跳转,搜索引擎会抓取新内容而保留旧的网址。因为服务器返回302代码,所以搜索引擎认为新的网址只是暂时的。
主要讲述redirect函数,函数时将HttpResponseRedirect和HttpResponsePermanentRedirect的功能进行完善和组合。 重定向类HttpResponseRedirect和HttpResponsePermanentRedirect分别代表HTTP状态码302和301。从redirect的定义过程可以看出,该函数运行原理如下:
在这里插入图片描述

  • 判断参数permanent的真假性来选择重定向的函数,若为True,则调用HttpResponsePermanentRedirect(301)来完成重定向过程,若为False,则调用HttpResponseRedirect(302)。
  • 由于HttpResponsePermanentRedirect和HttpResponseRedirect只支持路由地址的传入,因此函数redirect调用resolve_url方法对参数to进行判断。若参数to是路由地址,则直接将参数to的参数值返回;若参数to是路由命名,则使用reverse函数转换路由地址;若参数to是模型对象,则将模型转换成相应的路由地址(这种方法的使用频率相对较低)。

示例如下:

# index的urls.py
from django.urls import path
from . import views

urlpatterns = [
    # 定义首页的路由
    path('', views.index, name='index'),
    # 定义商城的路由
    path('shop', views.shop, name='shop')
]

# index的views.py
from django.http import HttpResponseRedirect
from django.http import HttpResponsePermanentRedirect
from django.shortcuts import reverse
from django.shortcuts import render, redirect

def index(request):
    return redirect('index:shop' ,permanent=True)
    # 设置302的重定向
    # url = reverse('index:shop')
    # return HttpResponseRedirect(url)
    # 设置301的重定向
    # return HttpResponsePermanentRedirect(url)

def shop(request):
    return render(request, 'index.html')

1.3 异常响应

异常响应是指HTTP状态码或500的响应状态,它与正常的响应过程(HTTP状态码为200的响应过程)是一样的,只是HTTP状态码有所不同,因此使用函数render作为响应过程,并且设置参数status的状态码(404或500)即可实现异常响应。
同一个网站的每种异常响应所返回的页面都是相同的,因此网站的异常响应必须适用于整个项目的所有应用。而在Django中配置全局的异常响应,必须在项目名的urls.py文件配置。以MyDjango为例,在MyDjango文件夹的urls.py中定义路由以及在index文件夹的views.py中定义视图函数,代码如下:

# MyDjango的urls.py
from django.urls import path, include
urlpatterns = [
    # 指向index的路由文件urls.py
    path('', include(('index.urls', 'index'), namespace='index')),
]
# 全局404页面配置
handler404 = 'index.views.pag_not_found'
# 全局500页面配置
handler500 = 'index.views.page_error'

# index的views.py
from django.shortcuts import render
def pag_not_found(request):
    """全局404的配置函数 """
    return render(request, '404.html', status=404)


def page_error(request):
    """全局500的配置函数 """
    return render(request, '500.html', status=500)

在MyDjango文件夹的urls.py里设置handler404和handler500,分别指向index文件夹的view.py的视图函数pag_not_found和page_error。当用户请求不合理或服务器发生异常时,Django就会根据请求信息执行相应的异常响应。视图函数分别使用404.html和500.html,因此在templates文件夹里新增404.html和500.html,代码如下:

# template文件夹下的404.html和500.html
# 404.html
<!DOCTYPE html>
<html>
<body>
<h3>这是404页面</h3>
</body>
</html>

# 500.html
<!DOCTYPE html>
<html>
<body>
<h3>这是500页面</h3>
</body>
</html>

上述内容是设置Django全局404和500的异常响应,只需在项目的urls.py中设置变量handler404和handler500。变量值是指向某个项目应用的视图函数,而被指向的视图函数需要设置相应的模板文件和响应状态码。

1.4 文件下载功能

响应内容除了返回网页信息外,还可以实现文件下载功能,是网站常用的功能之一。Django提供三种方式实现文件下载功能,分别是HttpResponse、StreamingHttpResponse和FileResponse,三者说明如下:

  • HttpResponse是所有响应过程的核心类,它的底层功能类是HttpResponseBase.
  • StreamingHttpResponse是在HttpResponseBase的基础上进行继承与重写的,它实现流式响应输出(流式响应输出是使用Python的迭代器将数据进行分段处理并传输的),适用于大规模数据响应和文件传输响应。
  • FileResponse是在StreamingHttpResponse的基础上进行继承与重写的,它实现文件的流式响应输出,只适用于文件传输响应。

以MyDjango为例,在MyDjango的urls.py、index的urls.py、view.py和templates的index.html中分别定义路由、视图函数和模板文件,代码如下:

# MyDjango的urls.py
from django.urls import path, include
urlpatterns = [
    # 指向index的路由文件urls.py
    path('', include(('index.urls', 'index'), namespace='index')),
]

# index的urls.py
from django.urls import path
from . import views

urlpatterns = [
    # 定义首页的路由
    path('', views.index, name='index'),
    path('download/file1', views.download1, name='download1'),
    path('download/file2', views.download2, name='download2'),
    path('download/file3', views.download3, name='download3'),
]

# index的views.py
from django.shortcuts import render
from django.http import HttpResponse, Http404
from django.http import StreamingHttpResponse
from django.http import FileResponse

def index(request):
    return render(request, 'index.html')

def download1(request):
    file_path = 'D:\cat.jpg'
    try:
        r = HttpResponse(open(file_path, 'rb'))
        r['content_type'] = 'application/octet-stream'
        r['Content-Disposition'] = 'attachment; filename=cat.jpg'
        return r
    except Exception:
        raise Http404('Download error')

def download2(request):
    file_path = 'D:\duck.jpg'
    try:
        r = StreamingHttpResponse(open(file_path, 'rb'))
        r['content_type'] = 'application/octet-stream'
        r['Content-Disposition'] = 'attachment; filename=duck.jpg'
        return r
    except Exception:
        raise Http404('Download error')

def download3(request):
    file_path = 'D:\dog.jpg'
    try:
        f = open(file_path, 'rb')
        r = FileResponse(f, as_attachment=True, filename='dog.jpg')
        return r
    except Exception:
        raise Http404('Download error')
        
# templates的index.html
<!DOCTYPE html>
<html>
<body>
<a href="{% url 'index:download1' %}">HttpResponse-下载</a>
<br>
<a href="{% url 'index:download2' %}">StreamingHttpResponse-下载</a>
<br>
<a href="{% url 'index:download3' %}">FileResponse-下载</a>
</body>
</html>

上述代码是整个MyDjango项目的功能代码,文件下载功能实现原理如下:

  • MyDjango的urls.py定义的路由指向index的urls.py文件。
  • index的urls.py定义4条路由信息,路由index是网站事业,路由所对应的视图函数index将模板文件index.html作为网页内容呈现在浏览器上。
  • 当在浏览器访问127.0.0.1:8000时,网页会出现3条地址链接,每条地址链接分别对应路由download1、download2和downloads3,这些路由所对应的视图函数分别使用不同的响应类实现文件下载功能。
  • 视图函数download1使用HttpResponse实现文件下载传入响应类HttpResponse进行实例化,并对实例化对象r设置参数content_type和Content-Disposition,这样就能实现文件下载功能。
  • 视图函数download2使用StrieamingHttpResponses实现文件下载,该类的使用方式与响应类HttpResponse的使用方式相同。
  • 视图函数download3使用FileResponse实现文件下载,该类的使用方式最为简单,只需将文件以字节流的方式读取并且设置参数as_attachment和filename,然后将三者一并传入FileResponse进行实例化即可。

上述例子说明三者之间存在一定差异,说明如下:

  • HttpResponse实现文件下载存在很大弊端,其工作原理是将文件读取并载入内存,然后输出到浏览器上实现下载功能。如果下载的文件较大,该方法就会占用很多内存。对于下载文件,Django推荐使用StreamingHttpResponse和FileResponse方法,这两个方法将下载文件分批写入服务器的本地磁盘,而不再将文件载入服务器的内存。
  • StreamingHttpResponse和FileResponse的实现原理是相同的,两者都是将下载文件分批写入本地磁盘,实现文件的流式响应输出。
  • 从适用范围来说,StreamingHttpResponse的适用范围更为广泛,可支持大规模数据或文件输出,而FileResponse只支持文件输出。
  • 从使用方式来说,由于StreamingHttpResponse支持数据或文件输出,因此在使用时需要设置响应输出类型和方式,而FileResponse只需设置3个参数即可实现文件下载功能。

2、HTTP请求对象

网站时根据用户请求来输出相应的响应内容,用户请求是指用户在浏览器上访问某个网址链接操作,浏览器会根据网址链接信息向网站发送HTTP请求,那么,当Django接收到用户请求时,它是如何获取用户请求信息的呢?

2.1 获取请求信息

当在浏览器上访问某个网址时,其实质时向网站发送一个HTTP请求,HTTP请求分为8种请求方式,每种请求方式的说明如下:

请求方式 说明
OPTIONS 返回服务器针对特定资源所支持的请求方法
GET 向特定资源发出请求(访问网页)
POST 向指定资源提交数据处理请求(提交表单、上传文件)
PUT 向指定资源位置上传数据内容跟
DELETE 请求服务器删除request-URL所标示的资源
HEAD 与GET请求类似,返回的响应中没有具体内容,用于获取报头
TRACE 回复和显示服务器收到的请求,用于测试和诊断
CONNECT HTTP/1.1协议中能够将连接改为管道方式的代理服务器

在上述的HTTP的请求方式里,最基本的时GET请求和POST请求,网站开发者关心的也只有GET请求和POST请求。GET请求和POST请求是可以设置请求参数的,两者的设置方式如下:

  • GET请求的请求参数是在路由地址后添加"?“和参数内容,参数内容以key=value形式表示,等号前面的是参数名,后面是参数值,如果涉及多个参数,每个参数之间旧使用”&"隔开,如127.0.0.1:8000/?user=xy&pw=123.
  • POST请求的请求参数一般以表单的形式传递,常见的额表单使用HTML的form标签,并且form标签的method属性设为POST。

2.2 文件上传功能

文件上传功能是网站开发常见的功能之一,比如上传图片和导入(音频文件、办公文件或安装包等)。无论上传的文件是什么格式的,其上传原理都是将文件二进制的数据格式读取并写入网站指定的文件夹里。通过一个简单的例子来使用Django实现文件上传功能。

# index的urls.py
from django.urls import path
from . import views

urlpatterns = [
    # 定义路由
    path('', views.upload, name='uploaded'),
]

# index的views.py
from django.shortcuts import render
from django.http import HttpResponse
import os

def upload(request):
    # 请求方法为POST时,执行文件上存
    if request.method == "POST":
        # 获取上传的文件,如果没有文件,则默认为None
        myFile = request.FILES.get("myfile", None)
        if not myFile:
            return HttpResponse("no files for upload!")
        # 打开特定的文件进行二进制的写操作
        f = open(os.path.join("D:\\upload", myFile.name), 'wb+')
        # 分块写入文件
        for chunk in myFile.chunks():
            f.write(chunk)
        f.close()
        return HttpResponse("upload over!")
    else:
        # 请求方法为GET时,生成文件上存页面
        return render(request, 'upload.html')

# templates的upload.html
<!DOCTYPE html>
<html>
<body>
<form enctype="multipart/form-data" action="" method="post">
   {% csrf_token %}
   <input type="file" name="myfile" />
   <br>
   <input type="submit" value="上存文件"/>
</form>
</body>
</html>

从视图函数upload可以看到,如果当前HTTP请求为POST,就会触发文件上传功能。其运行过程如下:

  • 模板文件upload.html使用from标签的文件控件file生成文件上传功能,该控件将用户上传的文件以二进制读取,读取方式由form标签的属性enctype="multipart/form-data"设置。
  • 浏览器将用户上传的文件读取后,通过HTTP的POST请求将二进制数据传到Django,当Django收到POST请求后,从请求对象的属性FILES获取文件信息,然后再D盘的upload文件夹里创建新的文件,文件名(从文件信息对象myFile.name获取)与用户上传的文件名相同。
  • 从文件信息对象myFile.chunks()读取文件内容,并写入upload文件夹的文件中,从而实现文件上传功能。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章