首页 > 编程语言 >Django框架之视图层,form表单文件上传下载,FBV与CBV引入,CBV源码剖析

Django框架之视图层,form表单文件上传下载,FBV与CBV引入,CBV源码剖析

时间:2024-06-22 16:59:06浏览次数:25  
标签:get self request 视图 源码 CBV POST data view

Ⅰ Django框架之视图层

【一】Django的三板斧

from django.shortcuts import HttpResponse, render, redirect, reverse

【1】HttpResponse

能返回字符串形式的数据

  • HttpResponse 是 Django 中用于创建 HTTP 响应对象的类,它允许你构建并返回服务器对客户端请求的数据和状态
  • 当需要直接返回纯文本数据(如 JSON 格式的数据)或者 HTML 页面时,可以使用 HttpResponse
from django.http import HttpResponse

def simple_view(request):
    data = {"message": "Hello, world!"}
    content = json.dumps(data)  # 将字典转换为 JSON 字符串
    response = HttpResponse(content, content_type="application/json")
    return response
  • 在这个例子中,视图函数 simple_view 创建了一个包含 JSON 数据的响应。

【2】render

能返回一个页面进行渲染

  • render 函数是 Django 的模板渲染机制的一部分,通常与模板引擎(如 Django 的默认模板引擎 - Jinja2 或者其它支持的模板引擎)一起使用。
  • 当你想要返回带有动态内容的 HTML 页面时,可以使用 render 函数将请求的数据与预定义的 HTML 模板结合起来:
from django.shortcuts import render
from .models import MyModel  # 假设有一个名为 MyModel 的模型

def detail_view(request, pk):
    obj = MyModel.objects.get(pk=pk)
    context = {'object': obj}  # 将模型实例添加到上下文(context)
    return render(request, 'my_template.html', context)
  • 在这个例子中,视图函数 detail_view 使用 render 函数从数据库获取 MyModel 实例,并将其作为参数传递给名为 my_template.html 的 HTML 模板进行渲染,最终返回给客户端。

【3】redirect

重定向至新的路由地址

  • redirect 函数用于实现网页间的重定向,即将用户从当前 URL 引导向另一个 URL。
  • 它不返回任何内容,而是引发一个 HTTP Redirection 错误,迫使客户端发送一个新的请求到指定地址:
from django.shortcuts import redirect

def login_required_view(request):
    if not request.user.is_authenticated:
        return redirect('login')  # 重定向到登录页面
    # 如果已登录,继续执行后续操作...
  • 在这个例子中,如果用户未登录,login_required_view 视图会调用 redirect 函数将用户重定向至名为 'login' 的URL对应的视图(通常是登录页面)。
  • 如果用户已登录,则继续执行原视图中的其他逻辑。

【4】reverse

解析路由的别名

【二】JsonResponse

  • 如果返回的是字典格式的数据
  • 我们在页面上看到的是 字典的键
  • 我们想看到的是字典 (json格式的字符串)

【1】返回的是字典格式的数据

from django.shortcuts import HttpResponse, render, redirect, reverse

def index(request):
    user_data = {
        "username": "silence",
        "password": "123456"
    }
    return HttpResponse(user_data)
# 我们在页面上看到的是 字典的键

【2】将字典格式的数据转换为 json 字符串

import json

from django.shortcuts import HttpResponse, render, redirect, reverse

def index(request):
    user_data = {
        "username": "silence",
        "password": "123456"
    }
    # 将字典格式的数据转换为 json 字符串
    user_data_str = json.dumps(user_data)
    # 再通过 HttpResponse 返回给前端
    # 前端看到的就是一个json格式的字符串
    return HttpResponse(user_data_str)
# {"username": "silence", "password": "123456"}

  • Content-Type: text/html; charset=utf-8
  • 是一个HTML页面
  • 我们想要的是json格式的数据 user_data.json

【3】增加一个额外的参数 content_type="application/json"

import json

from django.shortcuts import HttpResponse, render, redirect, reverse

def index(request):
    user_data = {
        "username": "silence",
        "password": "123456"
    }
    # 将字典格式的数据转换为 json 字符串
    user_data_str = json.dumps(user_data)
    # 增加一个额外的参数 content_type="application/json"
    # Content-Type: application/json
    # 前端接收到的数据类型就变成了  json 数据
    return HttpResponse(user_data_str, content_type="application/json")
# {
# "username": "silence",
# "password": "123456"
# }

Ⅱ form表单文件上传下载

form表单上传数据以及后端如何获取

【一】form知识回顾

<form action="" method="post" enctype="multipart/form-data"></form>
  • form表单想要上传文件类型的数据
    • method 参数必须改为post
    • enctype 参数必须指定成 form-data 类型

【二】数据准备

  • 路由urls.py
from django.contrib import admin
from django.urls import path
from user.views import index,register

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', index),
    path('register/', register,name='register'),
]
  • 前端register.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>注册</h1>

<form action="" method="post">
    <p>username:<input type="text" name="username"></p>

    <p>password:<input type="text" name="password"></p>

    <p>hobby:
        listen<input type="checkbox" name="hobby" value="listen">
        run<input type="checkbox" name="hobby" value="run">
        music<input type="checkbox" name="hobby" value="music">
    </p>

    <p>avatar:<input type="file" name="avatar"></p>

    <p><input type="submit" ></p>

    <p><input type="reset" ></p>

</form>



</body>
</html>

【三】数据处理

  • 默认的提交数据方式是 enctype="application/x-www-form-urlencoded"
  • 默认按照键值对提交数据 , 不会对键值对以外的数据进行额外的处理 文件数据

【1】POST请求数据和文件数据获取

  • csrf报错,记得注释掉settings里面的中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
from django.shortcuts import HttpResponse, render, redirect, reverse

def register(request):
    if request.method == 'POST':
        print(request.POST)
        username = request.POST.get('username')
        password = request.POST.get('password')
        hobby = request.POST.getlist('hobby')
        avatar = request.POST.getlist('avatar')

        print(f'username>>>>: {username}')
        print(f'password>>>>: {password}')
        print(f'hobby>>>>: {hobby}')
        print(f'avatar>>>>: {avatar}', type(avatar))

        return redirect(reverse('register'))
    return render(request, "register.html",locals())
username>>>>: silence
password>>>>: 741
hobby>>>>: ['listen', 'run']
avatar>>>>: ['sty.jpg'] <class 'list'>
  • 通过这种方式,我们只能获取到我们输入的文本数据,而拿不到我们想要的文件数据

【2】使用方法

  • 想要提交文件数据
  • enctype="multipart/form-data"
  • 键值对数据还是按照原本的方式进行提交
  • 文件对象会被单独处理
  • 到了后端提取数据的时候 键值对数据直接 get 文件数据就要用 request.FILES 提取
from django.shortcuts import HttpResponse, render, redirect, reverse

def register(request):
    if request.method == 'POST':
        print(request.POST)
        username = request.POST.get('username')
        password = request.POST.get('password')
        hobby = request.POST.getlist('hobby')
        avatar = request.POST.getlist('avatar')

        print(f'username>>>>: {username}')
        print(f'password>>>>: {password}')
        print(f'hobby>>>>: {hobby}')
        # 获取当前的图像文件,应该是一个文件数据(二进制类型)
        # 但是现在返回的是avatar>>>>: ['sty.jpg'] <class 'list'>
        print(f'avatar>>>>: {avatar}', type(avatar))


        # 获取数据传输中的 文件数据
        data_obj = request.FILES
        print(data_obj)
        # <MultiValueDict: {}> 空的
        

        return redirect(reverse('register'))
    return render(request, "register.html",locals())
  • 获取数据传输中的 文件数据
  • data_obj = request.FILES <MultiValueDict: {}> 空的
  • 修改 form 表单的数据提交方式 enctype="multipart/form-data"
from django.shortcuts import HttpResponse, render, redirect, reverse


def register(request):
    if request.method == 'POST':
        print(request.POST)
        username = request.POST.get('username')
        password = request.POST.get('password')
        hobby = request.POST.getlist('hobby')
        avatar = request.POST.getlist('avatar')

        print(f'username>>>>: {username}')
        print(f'password>>>>: {password}')
        print(f'hobby>>>>: {hobby}')
        # 获取当前的图像文件,应该是一个文件数据(二进制类型)
        # 但是现在返回的是avatar>>>>: ['sty.jpg'] <class 'list'>
        print(f'avatar>>>>: {avatar}', type(avatar))


        # 获取数据传输中的 文件数据
        data_obj = request.FILES
        print(data_obj)
        # <MultiValueDict: {'avatar': [<TemporaryUploadedFile: sty.jpg (image/jpeg)>]}>
        
        data = data_obj.get('avatar')
        print(data, type(data))
        # sty.jpg <class 'django.core.files.uploadedfile.TemporaryUploadedFile'>


        return redirect(reverse('register'))
    return render(request, "register.html",locals())
  • 想要保存文件数据 ---> 写入的文件数据是二进制数据才能被写入
from django.shortcuts import HttpResponse, render, redirect, reverse

def register(request):
    if request.method == 'POST':
        print(request.POST)
        username = request.POST.get('username')
        password = request.POST.get('password')
        hobby = request.POST.getlist('hobby')
        avatar = request.POST.getlist('avatar')

        print(f'username>>>>: {username}')
        print(f'password>>>>: {password}')
        print(f'hobby>>>>: {hobby}')
        # 获取当前的图像文件,应该是一个文件数据(二进制类型)
        # 但是现在返回的是avatar>>>>: ['sty.jpg'] <class 'list'>
        print(f'avatar>>>>: {avatar}', type(avatar))


        # 获取数据传输中的 文件数据
        data_obj = request.FILES
        print(data_obj)
        # <MultiValueDict: {'avatar': [<TemporaryUploadedFile: sty.jpg (image/jpeg)>]}>

        data = data_obj.get('avatar')
        print(data, type(data))
        # sty.jpg <class 'django.core.files.uploadedfile.TemporaryUploadedFile'>

         with open (data.name, 'wb') as fp:
             fp.write(data)
            # 不能直接写上面获取的对象

        return redirect(reverse('register'))
    return render(request, "register.html",locals())
  • 调用Django文件对象的内置方法 .chunks()
  • .chunks()提取文件对象中的二进制数据 参数是 每次提取多少个数据
import json

from django.shortcuts import HttpResponse, render, redirect, reverse

def index(request):
    user_data = {
        "username": "silence",
        "password": "123456"
    }
    # 将字典格式的数据转换为 json 字符串
    user_data_str = json.dumps(user_data)
    # 增加一个额外的参数 content_type="application/json"
    # Content-Type: application/json
    # 前端接收到的数据类型就变成了  json 数据
    return HttpResponse(user_data_str, content_type="application/json")


def register(request):
    if request.method == 'POST':
        print(request.POST)
        username = request.POST.get('username')
        password = request.POST.get('password')
        hobby = request.POST.getlist('hobby')
        avatar = request.POST.getlist('avatar')

        print(f'username>>>>: {username}')
        print(f'password>>>>: {password}')
        print(f'hobby>>>>: {hobby}')
        # 获取当前的图像文件,应该是一个文件数据(二进制类型)
        # 但是现在返回的是avatar>>>>: ['sty.jpg'] <class 'list'>
        print(f'avatar>>>>: {avatar}', type(avatar))


        # 获取数据传输中的 文件数据
        data_obj = request.FILES
        print(data_obj)
        # <MultiValueDict: {'avatar': [<TemporaryUploadedFile: sty.jpg (image/jpeg)>]}>

        data = data_obj.get('avatar')
        print(data, type(data))
        # sty.jpg <class 'django.core.files.uploadedfile.TemporaryUploadedFile'>

        # with open (data.name, 'wb') as fp:
        #     fp.write(data)
            # 不能直接写上面获取的对象


        with open(data.name, 'wb') as fp:
            for line in data.chunks():
                fp.write(line)



        return redirect(reverse('register'))
    return render(request, "register.html",locals())
  • 获得form表单下载的文件

【3】代码展示

  • views.py
import json

from django.shortcuts import HttpResponse, render, redirect, reverse

def index(request):
    user_data = {
        "username": "silence",
        "password": "123456"
    }
    # 将字典格式的数据转换为 json 字符串
    user_data_str = json.dumps(user_data)
    # 增加一个额外的参数 content_type="application/json"
    # Content-Type: application/json
    # 前端接收到的数据类型就变成了  json 数据
    return HttpResponse(user_data_str, content_type="application/json")


def register(request):
    if request.method == 'POST':
        print(request.POST)
        username = request.POST.get('username')
        password = request.POST.get('password')
        hobby = request.POST.getlist('hobby')
        avatar = request.POST.getlist('avatar')

        print(f'username>>>>: {username}')
        print(f'password>>>>: {password}')
        print(f'hobby>>>>: {hobby}')
        # 获取当前的图像文件,应该是一个文件数据(二进制类型)
        # 但是现在返回的是avatar>>>>: ['sty.jpg'] <class 'list'>
        print(f'avatar>>>>: {avatar}', type(avatar))


        # 获取数据传输中的 文件数据
        data_obj = request.FILES
        print(data_obj)
        # <MultiValueDict: {'avatar': [<TemporaryUploadedFile: sty.jpg (image/jpeg)>]}>

        data = data_obj.get('avatar')
        print(data, type(data))
        # sty.jpg <class 'django.core.files.uploadedfile.TemporaryUploadedFile'>

        # with open (data.name, 'wb') as fp:
        #     fp.write(data)
            # 不能直接写上面获取的对象


        with open(data.name, 'wb') as fp:
            for line in data.chunks():
                fp.write(line)



        return redirect(reverse('register'))
    return render(request, "register.html",locals())
  • register.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>注册</h1>

<form action="" method="post" enctype="multipart/form-data">
    <p>username:<input type="text" name="username"></p>

    <p>password:<input type="text" name="password"></p>

    <p>hobby:
        listen<input type="checkbox" name="hobby" value="listen">
        run<input type="checkbox" name="hobby" value="run">
        music<input type="checkbox" name="hobby" value="music">
    </p>

    <p>avatar:<input type="file" name="avatar"></p>

    <p><input type="submit" ></p>

    <p><input type="reset" ></p>

</form>



</body>
</html>
  • urls.py
from django.contrib import admin
from django.urls import path
from user.views import index,register

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', index),
    path('register/', register,name='register'),
]
  • sty.jpg

【4】模板步骤

def register(request):
    # 【1】form表单以 post 请求提交数据
    if request.method == 'POST':
        # 【2】form 表单中的键值对数据
        # print(request.POST)
        # Python中的 None 对应MySQL数据库中的 null
        # 【3】提取出键值对数据
        username = request.POST.get('username')
        password = request.POST.get('password')
        hobby = request.POST.getlist('hobby')
        # 【4】如果一个键对应多个值的情况要用getlist 提取数据
        avatar = request.POST.getlist('avatar')
        # 【5】获取当前的头像
        # ---> 如果提交数据方式是 enctype="application/x-www-form-urlencoded"
        # 获取到的是一个列表 列表里面存的是当前文件的文件名
        # print(f"avatar :>>>>{avatar, type(avatar)}")

        # 【6】修改 form 表单的数据提交方式 enctype="multipart/form-data"
        # 获取数据传输中的 文件数据
        data_obj = request.FILES
        # print(data_obj)
        # <MultiValueDict: {'avatar': [<InMemoryUploadedFile: Alist.png (image/png)>]}>

        # 【7】根据键将对应的文件数据对象提取出来
        data = data_obj.get("avatar")
        print(data, type(data))
        # 拿到文件数据对象 ---> 这个对象是Django的内置对象
        # Alist.png <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>

        # 【8】想要保存文件数据 ---> 写入的文件数据是二进制数据才能被写入
        # 因为上面的是Django的一个文件对象 不是二进制数据
        # with open(data.name, "wb") as fp:
        #     # 不能直接写上面获取到的对象
        #     fp.write(data)

        # 【9】调用Django文件对象的内置方法 .chunks()
        # 提取文件对象中的二进制数据 参数是 每次提取多少个数据
        with open(data.name, "wb") as fp:
            for line in data.chunks():
                fp.write(line)

        # 返回到原本的页面
        return redirect(reverse("register"))

    return render(request, "register.html", locals())

【四】总结request对象方法

【1】request.method

  • request.method:该方法返回客户端用于发起请求的HTTP方法。
  • 例如,可以是'GET'、'POST'、'PUT'、'DELETE'等。
  • 您可以使用该方法来确定请求的类型,并相应地执行特定操作。

【2】request.POST

  • request.POST:该属性是一个类似字典的对象,包含了请求中通过POST方法发送的所有参数。
  • 这些参数通常是通过HTML表单发送的。
  • 您可以使用参数的名字作为键来访问单个参数,例如request.POST['username']。

【3】request.GET

  • request.GET:类似于request.POST,该属性包含了请求中通过GET方法发送的所有参数。
  • 这些参数通常会附加在URL之后,以问号分隔。
  • 您可以使用参数的名字作为键来访问单个参数,例如request.GET['page']。

【4】request.FILES

  • request.FILES:该属性是一个类似字典的对象,包含了请求中通过文件上传组件发送的所有文件。
  • 当表单中包含文件上传字段时,通过request.FILES可以访问上传的文件。
  • 您可以使用文件的名字作为键来访问单个文件,例如request.FILES['file']。

【5】request.path

只能获取到路由地址,无法获取到参数

【6】request.path_info

只能获取到路由地址,无法获取到参数

  • 用于表示请求URL的路径部分,不包括域名和查询参数。
  • 与 request.path 相比,request.path_info 更加原始和未经解析。
  • 它保留了URL路径中的任何编码、特殊字符或斜杠等信息。
  • 例如,对于以下请求URL:"http://example.com/foo/bar/?page=2",request.path_info 的值将是 "/foo/bar/"。
  • 通常情况下,您可以使用 request.path 来获取丢弃域名后的路径,而使用 request.path_info 来获取原始的、未解析的路径。这在某些情况下非常有用,例如当您需要对URL进行一些自定义操作或路由处理时。

【7】request.get_full_path()

即能获取到路由地址又能获取到完整的路由地址后面的参数

【8】request方法补充

<!--method="get"-->
<form action="" method="get" enctype="multipart/form-data">
  • views.py
import json

from django.shortcuts import HttpResponse, render, redirect, reverse

def login(request):
    print(request.GET)
    # <QueryDict: {'username': ['silence'], 'password': ['741'], 'hobby': ['listen', 'run'], 'avatar': ['sty.jpg']}>

    print(request.FILES)
    # <MultiValueDict: {}>
    # 提交的请求方式是 GET 请求
    # form 表单的数据格式 enctype="multipart/form-data"
    # 后端只能接收到键值对数据 无法接收到 文件数据

    # POST 请求在浏览器的地址栏中你会看到数据吗? 看不到 只能看到127.0.0.1:8000/register/
    #  GET 请求在浏览器地址栏中你会看到数据吗?  可以看到  GET 请求携带的请求体数据大小是有限制的!5MB

    # http://localhost:8000/login/?username=silence&password=741&hobby=listen&hobby=run&avatar=sty.jpg

    # 想获取 GET 请求提交的数据
    # request.GET.get('key')
    # request.GET.getlist('key')

    print(request.path)  # /login/
    print(request.get_full_path())
    # /login/?username=silence&password=741&hobby=listen&hobby=run&avatar=sty.jpg

    print(request.path_info)
    # /login/
    print(request.get_full_path_info())
    # /login/?username=silence&password=741&hobby=listen&hobby=run&avatar=sty.jpg


    return render(request,'login.html',locals())
  • login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>登陆页面</h1>

<form action="" method="get" enctype="multipart/form-data">
    <p>username:<input type="text" name="username"></p>

    <p>password:<input type="text" name="password"></p>

    <p>hobby:
        listen<input type="checkbox" name="hobby" value="listen">
        run<input type="checkbox" name="hobby" value="run">
        music<input type="checkbox" name="hobby" value="music">
    </p>

    <p>avatar:<input type="file" name="avatar"></p>

    <p><input type="submit" ></p>

    <p><input type="reset" ></p>

</form>


</body>
</html>

Ⅲ FBV与CBV引入

  • 视图函数既可以是函数也可以是类

  • 我们之前写过的都是基于函数的view,就叫FBV

  • 还可以把view写成基于类的,就叫CBV

  • FBV function 函数

  • CBV class 类

【一】FBV和CBV

  • FBV(Function-Based Views)与 CBV(Class-Based Views)是 Django 框架中用于处理视图(Views)的两种不同的编程方式。

【1】FBV

# FBV版添加班级
def add_class(request):
    if request.method == "POST":
        class_name = request.POST.get("class_name")
        models.Classes.objects.create(name=class_name)
        return redirect("/class_list/")
    return render(request, "add_class.html")

【2】CBV

(1)路由

# CBV 路由  -  根据请求方式的不同选择不同的入口动作
path('login/', views.MyLogin.as_view())

(2)视图

from django.views import View


class MyLogin(View):
    def get(self, request, *args, **kwargs):
        return HttpResponse("get")

    def post(self, request, *args, **kwargs):
        return HttpResponse("post")
  • FBV和CBV各有各的特点,都有应用
  • CBV特点
    • 能够直接根据请求方式的不同直接匹配到对应的方法执行

【二】Class-Based Views(CBV)使用步骤

  • CBV 是 Django 提供的另一种视图编程方式,引入了面向对象的概念。
  • 使用类来表示视图,并使用类中的方法来处理不同类型的HTTP请求。
  • Django 提供了许多内置的 CBV 类,可以方便地扩展和重写这些类来实现自定义功能。

【1】引入一个类

from django.views import View

【2】创建一个类 , 继承上面那个类

class HomeView(View):

【3】在自定义类里面定义两个方法

# def get(self, request) : 当前端发送 get 请求的时候会被触发
# def post(self, request) : 当前端发送 post 请求的时候会被触发

    def get(self, request):
        print(f'触发GET请求!')
        return render(request,'home.html',locals())
    
        def post(self, request):
        # 【5】提交 post 请求
        print(request.POST)
        return redirect(reverse("home"))

【4】配置路由映射关系

# 中间的视图函数变成了 类.as_view()
path('home/', HomeView.as_view(),name='home'),

【5】提交 post 请求

print(request.POST)
        return redirect(reverse("home"))

【6】总代码展示

  • views.py
from django.shortcuts import HttpResponse, render, redirect, reverse
# 【1】导入Django的内置类
from django.views import View

# 【2】自定义类继承Django的内置类
class HomeView(View):
# 【3】在类内部写视图函数
    def get(self, request):
        print(f'触发GET请求!')
        return render(request,'home.html',locals())


    def post(self, request):
        # 【5】提交 post 请求
        print(request.POST)
        return redirect(reverse("home"))
  • urls.py
from django.urls import path
from user.views import index,register,login,HomeView

urlpatterns = [
# 【4】定义路由映射 函数就变成了 类.as_view()
    path('home/', HomeView.as_view(),name='home'),
]
  • login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>登陆页面</h1>

<form action="" method="get" enctype="multipart/form-data">
    <p>username:<input type="text" name="username"></p>

    <p>password:<input type="text" name="password"></p>

    <p>hobby:
        listen<input type="checkbox" name="hobby" value="listen">
        run<input type="checkbox" name="hobby" value="run">
        music<input type="checkbox" name="hobby" value="music">
    </p>

    <p>avatar:<input type="file" name="avatar"></p>

    <p><input type="submit" ></p>

    <p><input type="reset" ></p>

</form>


</body>
</html>

Ⅳ CBV源码剖析

  • Django请求声明周期
# 【1】从前端入手 前端发送请求
# 【2】wsgiref 模块上对 Request 进行分装
# 【3】Django框架上 中间件 (相当于你的公司的保安 你的第一道防线)
# 【4】Django框架上的 路由映射  urls.py 去根据路径映射制定的视图函数
# 【5】Django框架上的 视图函数 views.py 中
# (1)获取前端页面 (模版语法渲染)
# (2)读取数据库数据 (增删改查)
# (3)记录你的操作日志 (记录日志)
# 返回 response对象
# 【6】Django框架上的 urls.py 根据制定路径回去
# 【7】回到了 wsgiref 模块上 对Django的 response 对象进行封装
# 【8】回到前端解包渲染

【一】分析入口

# 当前端的请求进入到Django之后会先经过 中间件 (我没写东西所以不管)
# 紧接着就走到了路由层 路由映射关系 urls.py  中
path('home/', HomeView.as_view(),name='home'),

【二】思考

# 为什么 要用 类.as_view()
# as_view 属于类的绑定方法
# as_view() ---> 一定有一个返回值

# 原来写的是函数名 --->现在变成了as_view() --->as_view() 返回了什么?

【三】找到了入口 as_view

CTRL加鼠标左键  点击path('home/', HomeView.as_view(),name='home'),里的as_view()
@classonlymethod
    def as_view(cls, **initkwargs):
        """Main entry point for a request-response process."""
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError(
                    'The method name %s is not accepted as a keyword argument '
                    'to %s().' % (key, cls.__name__)
                )
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

【1】确定了 as_view 是类的绑定方法,类可以直接调用不需要传递额外的参数

@classonlymethod
def as_view(cls, **initkwargs):

# 确定了 as_view 是类的绑定方法 类可以直接调用不需要传递额外的参数

【2】**initkwargs

我在调用 HomeView.as_view() 的时候没有传递参数 所以**initkwargs 没东西

【3】http_method_names是什么

CTRL加鼠标左键  点击http_method_names
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
# 如果传递额外的参数进来了 并且在上面的列表中
if key in cls.http_method_names:
            # 就会报错
            raise TypeError(
                'The method name %s is not accepted as a keyword argument '
                'to %s().' % (key, cls.__name__)
            )
# 现在知道http_method_names是什么了  不走这些代码

【4】hasattr

# hasattr 判断当前对象中是否存在指定的属性 有就是 True 没有 False
		if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))
# 我没有传递参数 所以这一步也不走 继续向下执行代码

【5】view

# 发现 initkwargs 没东西
# 就继续向下走 就走到了内嵌函数 view
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            self.setup(request, *args, **kwargs)
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

(1)cls(**initkwargs)

def view(request, *args, **kwargs):
        # 调用当前类 实例化得到一个当前类的对象
        # cls ---> 当前的自定义类 HomeView
        
        # path('home/', HomeView.as_view(),name='home'),
        # 由于 HomeView 没有 __init__ 方法 所以初始化的时候就不需要传递额外的参数
        # self 就是当前 HomeView 类的一个对象
        self = cls(**initkwargs)

(2)setup

# HomeView 类里面没有 setup 方法
# 于是就去父类 View 里面找 向下找
# 为自己的对象初始化属性并且 保证我一定有 get 方法
self.setup(request, *args, **kwargs)
def setup(self, request, *args, **kwargs):
        """Initialize attributes shared by all view methods."""
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
从as_view 的 内嵌函数 view 函数 进入到当前的 setup
# 从as_view 的 内嵌函数 view 函数 进入到当前的 setup
def setup(self, request, *args, **kwargs):
    # (1)hasattr(self, 'get') 判断自己是否有 get 属性
    # 有 自己定义的get方法
    # (2)not hasattr(self, 'head') 没写过 head
    if hasattr(self, 'get') and not hasattr(self, 'head'):
        # 增加了一个属性 head 属性
        # self.get : 自己的 get 函数的内存地址
        self.head = self.get
    # (3)自己有定义def get(self, request):
    # 把 request 对象初始化进自己的属性中
    self.request = request
    # (4)def get(self, request):
    # def get(self, request, *args, **kwargs):自己知道*args, **kwargs一定没有参数所以没写
    # 如果有位置参数及关键字参数则添加到自己的属性中
    self.args = args
    self.kwargs = kwargs

(3)执行完setup就又回到了原处

 # 为自己的对象初始化属性并且 保证我一定有 get 方法

self.setup(request, *args, **kwargs)

(4)request

  • 如果 自己没有 request 属性,就报错了
# 如果 自己没有 request 属性
        if not hasattr(self, 'request'):
            # 就报错了
            raise AttributeError(
                "%s instance has no 'request' attribute. Did you override "
                "setup() and forget to call super()?" % cls.__name__
            )
  • 但是在setup里有初始化request,所以不写也不会报错
# 把 request 对象初始化进自己的属性中
    self.request = request

(5)return self.dispatch(request, *args, **kwargs)

self.dispatch(request, *args, **kwargs)
        # 调用了自己的 dispatch 方法
        #  dispatch 方法  有一个返回值 并且 这个返回值一定是 Django的 response 对象
        
        #  path('login/', login, name="login"), login 是一个函数地址 能被正常响应
        #  path('home/', HomeView.as_view(), name="home"), HomeView.as_view() 返回的一定也是函数地址 view 的函数地址
        # self.dispatch(request, *args, **kwargs) 返回的也是函数内存地址
从as_view 的 内嵌函数 view 函数 进入到当前的 dispatch
# 【6】从as_view 的 内嵌函数 view 函数 进入到当前的 dispatch
def dispatch(self, request, *args, **kwargs):
    # (1)request.method.lower()  : 当前浏览器提交的请求方式的小写
    # http_method_names指的是HTTP请求中支持的方法(methods)的列表:包括GET,POST,HEAD...
    # request.method.lower()将GET变为小写get 所以一定在 self.http_method_names
    if request.method.lower() in self.http_method_names:
        # getattr 从对象中映射出指定键对应的属性值
        # 如果能拿到对应键对应的函数内存地址则就是函数内存地址
        # request.method.lower()相当于get 如果拿不到函数内存地址 就去后面拿
        # self.http_method_not_allowed CTRL加鼠标左键看其属性(解释在下方)
        
        # 否则就是一个 HttpResponseNotAllowed 对象 提示当前请求方法不被允许
        handler =getattr(self,request.method.lower(),self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
self.http_method_not_allowed
# 如果请求方式是在允许的范围内 就会正常响应
# 如果不是正确的请求方式就会提示 当前请求方式不被允许
   
   def http_method_not_allowed(self, request, *args, **kwargs):
        logger.warning(
            'Method Not Allowed (%s): %s', request.method, request.path,
            extra={'status_code': 405, 'request': request}
        )
        return HttpResponseNotAllowed(self._allowed_methods())
handler()
   # handler : get 函数的内存地址
    # handler() : get 函数加 ()
    return handler(request, *args, **kwargs)

(6)执行完dispatch又返回原处接受到 self.dispatch 返回的数据 HttpResponse对象

# 当请求进来的时候会触发 view 函数执行
# 接受到 self.dispatch 返回的数据 DjangoResponse对象

return self.dispatch(request, *args, **kwargs)

(7)返回view对象

# 初始化类属性		
    	view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

【四】小总结

分析:

  • 人口请求打进来路由层的时候一定触发.as_view()
  • 进入.as_view(),按住CTRL加鼠标左键进去
    • 点进去看进入as_view()函数,由于没有传参数,**initkwargs就没有值,所以下方代码不走
    • 然后往下走 调用view(),view()最后的返回值是view
    • 然后就可以确定as_view()接收到的就是view,就是内嵌函数def view(request, *args, **kwargs):的内存地址
  • 当请求进来的时候触发HomeView.as_view()的执行,就是view函数的执行
    • 点进去看def view(request, *args, **kwargs):
    • 执行self = cls(**initkwargs)代码 得到当前类的一个对象
    • 然后调用类的setup方法,自己没有定义setup方法,需要去父类里面查看setup方法
    • 就是当前这个类,然后往下找就找到了def setup(self, request, *args, **kwargs):这个方法
      • 进入之后在里面判断我有get方法并且没有head方法
      • 并且会初始化head,self.head = self.get,将head方法替换成我的get方法
      • 初始化request对象
      • 如果自己携带了参数,初始化携带的位置参数以及关键字参数,self.args = args,self.kwargs = kwargs
      • 走完然后返回
    • 然后校验自己有没有request对象,没有就报错
    • 在之后就进去dispatch方法,return self.dispatch(request, *args, **kwargs)
    • 自己没有定义dispatch方法,需要去父类里面查看dispatch方法
    • 就是当前这个类,然后往下找就找到了def dispatch(self, request, *args, **kwargs):这个方法
      • 判断自己这个方法在不在允许的方法内if request.method.lower() in self.http_method_names:
        • 在允许方法内
          • 映射出来这个方法的内存地址,把他赋给handlerhandler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        • 不在允许方法内
          • 返还一个不允许的提示,HttpResponseNotAllowed(self._allowed_methods())
      • 再往下走到,return handler(request, *args, **kwargs)
      • handler:get方法调用
      • handler(request, *args, **kwargs) ,返回的应该是 HttpResponse对象,或者是render,….三剑客里面的
      • return handler(request, *args, **kwargs) 返回响应对象
    • 然后返回到dispatch
    • 然后return self.dispatch(request, *args, **kwargs)再将响应对象返回出去
    • 然后前端就接收到了,就能将其渲染了

【五】总的代码加步骤

# 【一】分析入口
# 当前端的请求进入到Django之后会先经过 中间件 (我没写东西所以不管)
# 紧接着就走到了 路由映射关系 urls.py  中
# path('home/', HomeView.as_view(), name="home"),

# 【二】思考
# 为什么 要用 类.as_view()
# as_view 属于类的绑定方法
# as_view() ---> 一定有一个返回值

# 原来写的是函数名 ---> as_view() --->as_view() 返回了什么?

# 【三】找到了入口 as_view
'''
# 【一】确定了 as_view 是类的绑定方法 类可以直接调用不需要传递额外的参数
@classonlymethod
def as_view(cls, **initkwargs):
    # 【二】**initkwargs 我在调用
    # 【1】HomeView.as_view() 的时候没有传递参数 **initkwargs 没东西
    for key in initkwargs:
        # 【2】http_method_names : 知道这个东西是什么 不走
        #  http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
        # 如果传递额外的参数进来了 并且在上面的列表中
        if key in cls.http_method_names:
            # 就报错了
            raise TypeError(
                'The method name %s is not accepted as a keyword argument '
                'to %s().' % (key, cls.__name__)
            )
        # 【3】hasattr 判断当前对象中是否存在指定的属性 有就是 True 没有 False
        if not hasattr(cls, key):
            raise TypeError("%s() received an invalid keyword %r. as_view "
                            "only accepts arguments that are already "
                            "attributes of the class." % (cls.__name__, key))

    # 【三】发现 initkwargs 没东西
    # 就继续向下走 就走到了内嵌函数 view
    def view(request, *args, **kwargs):
        # 【1】调用当前类 实例化得到一个当前类的对象
        # cls ---> 当前的自定义类 HomeView
        # 由于 HomeView 没有 __init__ 方法 所以初始化的时候就不需要传递额外的参数
        # self 就是当前 HomeView 类的一个对象
        self = cls(**initkwargs)
        # 【2】HomeView 类里面没有 setup 方法
        # 于是就去父类 View 里面找 向下找
        # 为自己的对象初始化属性并且 保证我一定有 get 方法
        self.setup(request, *args, **kwargs)
        # 【4】回到了这里
        # 如果 自己没有 request 属性
        if not hasattr(self, 'request'):
            # 就报错了
            raise AttributeError(
                "%s instance has no 'request' attribute. Did you override "
                "setup() and forget to call super()?" % cls.__name__
            )

        # 【5】self.dispatch(request, *args, **kwargs)
        # 调用了自己的 dispatch 方法
        #  dispatch 方法  有一个返回值 并且 这个返回值一定是 Django的 response 对象
        #  path('login/', login, name="login"), login 是一个函数地址 能被正常响应
        #  path('home/', HomeView.as_view(), name="home"), HomeView.as_view() 返回的一定也是函数地址 view 的函数地址
        # self.dispatch(request, *args, **kwargs) 返回的也是函数内存地址
        # 【7】当请求进来的时候会触发 view 函数执行
        # 接受到 self.dispatch 返回的数据 HttpResponse对象
        return self.dispatch(request, *args, **kwargs)

    # 初始化类属性
    view.view_class = cls
    view.view_initkwargs = initkwargs
    update_wrapper(view, cls, updated=())
    update_wrapper(view, cls.dispatch, assigned=())
    return view

# 【3】从as_view 的 内嵌函数 view 函数 进入到当前的 setup
def setup(self, request, *args, **kwargs):
    # (1)hasattr(self, 'get') 判断自己是否有 get 属性
    # 有 自己定义的get方法
    # (2)not hasattr(self, 'head') 没写过 head
    if hasattr(self, 'get') and not hasattr(self, 'head'):
        # 增加了一个属性 head 属性
        # self.get : 自己的 get 函数的内存地址
        self.head = self.get
    # (3)吧 request 对象初始化尽自己的属性中
    self.request = request
    # (4)如果有位置参数及关键字参数则添加到自己的属性中
    self.args = args
    self.kwargs = kwargs

# 【6】从as_view 的 内嵌函数 view 函数 进入到当前的 dispatch
def dispatch(self, request, *args, **kwargs):
    # (1)request.method.lower()  : 当前浏览器提交的请求方式的小写
    # GET ---> get 一定在 self.http_method_names
    if request.method.lower() in self.http_method_names:
        # getattr 从对象中映射出指定键对应的属性值
        # 如果能拿到对应键对应的函数内存地址则就是函数内存地址
        # 否则就是一个 HttpResponseNotAllowed 对象 提示当前请求方法不被允许
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
    # (2)handler : get 函数的内存地址
    # handler() : get 函数加 ()
    return handler(request, *args, **kwargs)

# 如果请求方式是在允许的范围内 就会正常响应
# 如果不是正确的请求方式就会提示 当前请求方式不被允许
def http_method_not_allowed(self, request, *args, **kwargs):
    logger.warning(
        'Method Not Allowed (%s): %s', request.method, request.path,
        extra={'status_code': 405, 'request': request}
    )
    return HttpResponseNotAllowed(self._allowed_methods())

'''

标签:get,self,request,视图,源码,CBV,POST,data,view
From: https://www.cnblogs.com/zyb123/p/18262490

相关文章

  • 基于springboot实现知识管理系统项目【项目源码+论文说明】计算机毕业设计
    摘要随着信息互联网信息的飞速发展,无纸化作业变成了一种趋势,针对这个问题开发一个专门适应师生作业交流形式的网站。本文介绍了知识管理系统的开发全过程。通过分析企业对于知识管理系统的需求,创建了一个计算机管理知识管理系统的方案。文章介绍了知识管理系统的系统分析......
  • Java计算机毕业设计超市管理系统(开题报告+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着现代商业的快速发展,超市作为零售业的重要组成部分,其管理效率和运营水平直接影响到企业的竞争力和市场地位。然而,传统的超市管理方式往往存在效率......
  • 基于Java中的SSM框架实现一汽租车共享平台系统项目【项目源码+论文说明】计算机毕业设
    摘要随着人们生活水平的不断提高,人们租车进行旅游的行为已成为大家的不二选择。汽车租赁服务被称为交通运输服务行新兴的服务行业,因为汽车租赁无须办理保险、无须年检维修、车型可随意更换等优点,以租车代替买车来控制企业成本,其实这种汽车管理方式在外企中是十分流行的方......
  • 基于springboot实现酒店客房管理系统项目【项目源码+论文说明】计算机毕业设计
    摘 要随着人们的物质水平的提高,旅游业和酒店业发展的速度越来越快。近年来,市面上酒店的数量和规模都在不断增加,如何提高酒店的管理效率和服务质量成为了一个重要的问题。伴随着信息技术的发展,基于互联网的酒店客房管理系统已经成为了酒店管理过程中的一个重要的手段。这......
  • 分享一个go源码的均匀采样底层实现原理
    //int31n也就是下面这个函数,跟上面Int31n效果是一样的.但是效率更高.算法不一样.这个算法非常精彩,效率也更高.//int31nreturns,asanint32,anon-negativepseudo-randomnumberinthehalf-openinterval[0,n).//nmustbe>0,butint31ndoesnotcheckthis;......
  • C#.net6.0语言+B/S架构+前后端分离 手术麻醉信息管理系统源码
    C#.net6.0语言+B/S架构+前后端分离手术麻醉信息管理系统源码什么是手术麻醉信息管理系统满足医院等级评级需求满足电子病历评级需求满足科室需求术前1、患者术前评估/诊断2、术前讨论制定手术方案3、手术准备4、术前准备术中1、送手术室2、麻醉前3、手术术后1......
  • Java计算机毕业设计博物馆管理系统(开题报告+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展,传统博物馆的管理模式已经难以适应现代社会的需求。博物馆作为重要的文化传承和展示机构,需要更加高效、智能的管理系统来支撑......
  • Spring Boot 源码分析五:Spring Boot AutoConfiguration 自动配置机制
    1.引言在前几篇文章中,我们探讨了SpringBoot的启动流程及其扩展机制。在本篇文章中,我们将深入分析SpringBoot的自动配置(AutoConfiguration)机制,这是SpringBoot最具特色和强大的功能之一。2.自动配置概述SpringBoot的自动配置机制旨在根据项目中的类路径和配置属性,自......
  • java毕业设计之在线考试系统(springboot完整源码+说明文档+演示视频)
    1项目介绍本系统主要包括管理员和用户两个角色组成;主要包括首页、个人中心、用户管理、教师管理、课程信息管理、班级信息管理、试题管理、在线试题管理、考试管理等功能的管理系统。2、项目技术项目后端框架:Java+ssm项目前端框架:vue2,ssm3、开发环境springboot环境......
  • C/C++ 堆栈stack算法详解及源码
    堆栈(stack)是一种常见的数据结构,具有"先进后出"(LastInFirstOut,LIFO)的特性。堆栈算法允许在堆栈顶部进行元素的插入和删除操作。堆栈的操作包括:入栈(Push):将元素添加到堆栈的顶部。出栈(Pop):从堆栈的顶部移除元素。取栈顶元素(Top):获取堆栈顶部的元素,但不对其进行删除操作。......