51. django之视图层_JsonResponse_request补充_CBV

1. 视图层三个函数

1.1 HttpResponse

[1] 概念

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

[2] 返回纯文本类型数据

1.django项目下新建一个应用app01:  startapp app01

2.在app01的views.py中定义视图函数

from django.shortcuts import render, HttpResponse

# Create your views here.

def http_r_test(request, *args, **kwargs):
    return HttpResponse('from http_r_test view')  # 返回纯文本数据

3.在app01的urls.py中定义路由

from django.urls import path
from app01.views import http_r_test

urlpatterns = [
    path('http_r_test/', http_r_test, name='http_r_test1')
]

4.将app01的路由添加到项目路由

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),
]

5.前端访问时即可返回纯文本类型数据

image

[3] 返回JSON格式的数据

1. 直接将字典给HttpResponse,返回的数据是字典的键

app01的views.py

def http_r_test(request, *args, **kwargs):
    data = {'name': 'avril', 'grade': 2}
    return HttpResponse(data)

image

2. 使用前端的内容类型content_type,application/json用于传输结构化的 JSON 数据

 先将字典转换为json格式数据,再将json数据返回

def http_r_test(request, *args, **kwargs):
    data = {'name': 'avril', 'grade': 2}
    data1 = json.dumps(data)
    return HttpResponse(data1, content_type='application/json')

image

image

1.2 render

[1] 概念

render 函数是 Django 的模板渲染机制的一部分,通常与模板引擎(如 Django 的默认模板引擎 - Jinja2 或者其它支持的模板引擎)一起使用。
当想要返回带有动态内容的 HTML 页面时,可以使用 render 函数将请求的数据与预定义的 HTML 模板结合起来

[2] 以指定的键值对传递数据

render函数的参数如下:render(request, template_name, context=None, content_type=None, status=None, using=None)

request:视图函数中的request对象

template_name:前端文件名

context:上下文对象

content_type:内容类型,响应数据格式

 

1. app01的view.py中定义视图函数

def render_test(request, *args, **kwargs):
    info = {'name': 'lavigne', 'grade': 3}
    return render(request, index.html, info)

2.应用app01下新建templates目录,该目录下新建index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>信息如下</p>
    <p>总信息:{{ info }}</p>
    <p>姓名:{{ name }}</p>
    <p>年级:{{ grade }}</p>
</body>
</html>

3. app01的urls.py定义路由

from django.urls import path
from app01.views import render_test

urlpatterns = [
    path('render_test/', render_test, name='render_test1')
]

4. 将路由添加到项目路由

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),
]

5. 前端通过键获取值

  通过{{ info }}上下文对象获取不到值,可以通过上下文对象字典的键获取值

image

[3] 加载局部名称空间

查看全局名称空间 globals()
查看局部名称空间 locals()
局部修改全局名称空间 global
内嵌修改局部名称空间 nonlocal

 

locals() 会将当前视图函数中定义的所有局部变量(如 name、age、hobby)自动打包成一个字典,作为 context 传递给模板。

举例:

from django.shortcuts import render

def index(request):
    # 定义任意局部变量
    name = "小明"
    age = 20
    hobby = ["篮球", "编程"]
    
    # locals() 自动把上面所有变量打包成字典传给模板
    return render(request, 'index.html', locals())

前端模板直接使用

<h1>{{ name }}</h1>
<p>{{ age }}</p>
<p>{{ hobby.0 }}</p>

 

将app01视图函数render的上下文对象参数context=info修改为locals( )

def render_test(request, *args, **kwargs):
    info = {'name': 'lavigne', 'grade': 3}
    return render(request, 'index.html', locals())

原来的字典已经变成了locals打包后字典的一个键,无法通过原字典的键直接获取值

image

可以通过原字典调用键获取值

修改app01的index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>信息如下</p>
    <p>总信息:{{ info }}</p>
    <p>姓名:{{ info.name }}</p>
    <p>年级:{{ info.grade }}</p>
</body>
</html>

image

1.3 redirect

redirect无补充内容

2. JsonResponse

2.1 概念

通过HttpResponse返回json格式数据需要两步
def http_r_test(request, *args, **kwargs):
    data = {'name': 'avril', 'grade': 2}
    data1 = json.dumps(data)
    return HttpResponse(data1, content_type='application/json')
JsonResponse的作用:

  自动序列化:直接传入 Python 的字典或列表,JsonResponse 会自动调用 json.dumps() 将其转换为 JSON 字符串,无需手动处理。

  正确的内容类型:自动设置 HTTP 响应头 Content-Type: application/json,明确告知客户端返回的是 JSON 数据。

  简洁易用:简化 Django 返回 JSON 数据的代码,避免手动处理序列化和响应头

2.2 代码

[1] 前言

1.1[3]返回json格式数据基础上当返回中文时会被转换为ASCII码

 app01的views.py中

def http_r_test(request, *args, **kwargs):
    data = {'name': 'avril加拿大', 'grade': 2}
    data1 = json.dumps(data)
    return HttpResponse(data1, content_type='application/json')

image

修改json的ensure_ascii=False即可

image

[2] 使用JsonResponse直接返回字典

app01的views.py中定义函数

from django.shortcuts import render, HttpResponse
from django.http import JsonResponse

# Create your views here.

def json_re_test(request, *args, **kwargs):
    data = {'name': 'avril加拿大', 'age': 19}
    return JsonResponse(data)

app01的urls.py中定义路由

from django.urls import path
from app01.views import json_re_test

urlpatterns = [
    path('json_re_test/', json_re_test, name='json_re_test1'),
]

添加到项目路由

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),
]

前端仍然是ASCII码

image

[3] 给JsonResponse设置参数

JsonResponse是基于HttpResponse重写的一个方法

json_dumps_params设置一个字典{"ensure_ascii": False}

def json_re_test(request, *args, **kwargs):
    data = {'name': 'avril加拿大', 'age': 19}
    return JsonResponse(data, json_dumps_params={'ensure_ascii': False})

 

中文不再转换为ASCII码

image

3. form表单文件上传下载

3.1 form知识回顾

<form action="" method="post" enctype="multipart/form-data"></form>
form表单想要上传文件类型的数据:
  method 参数必须改为post
  enctype 参数必须指定成 form-data 类型(如果不写则只能传文件名)

 

form标签里默认参数(不用写)enctype="application/x-www-form-urlencoded"

作用:将所有的键值对传递给后端,对文件数据不作处理,只传递文件名的字符串

3.2 只上传文件名称不上传内容

 django项目下新建static目录,用来存放bootstrap、jQuery静态文件

image

在settings.py中加载静态文件系统

image

app01的views.py中定义register函数

from django.shortcuts import render, redirect

# Create your views here.

def register(request, *args, **kwargs):
    # 如果是get请求,返回注册页面
    if request.method == 'GET':
        return render(request, 'register.html', locals())
    else:
        # 当前端请求是post提交数据时,先打印请求数据,重定向到注册路由,即刷新了页面
        print(request.POST)
        return redirect('register1')

app01的urls.py中定义路由

urlpatterns = [
    path('register/', register, name='register1'),
]

项目路由里添加路由

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),
]

应用app01的templates目录定义register.html,使用静态文件系统

复制form表单的代码https://v3.bootcss.com/css/#forms,将form表单放在栅格系统内,对代码稍作修改

form标签内以post提交数据

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="{% static 'jquery/jquery.js' %}"></script>
    <link rel="stylesheet" href="{% static 'bootstrap/bootstrap.css' %}">
    <script src="{% static 'bootstrap/bootstrap.js' %}"></script>

</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            {# post为提交数据 #}
            <form action="" method="post">
                <div class="form-group">
                    <label for="InputUsername">Username</label>
                    <input type="text" name="username" class="form-control" id="InputUsername" placeholder="Username">
                </div>
                <div class="form-group">
                    <label for="InputPassword">Password</label>
                    <input type="password" name="password" class="form-control" id="InputPassword"
                           placeholder="Password">
                </div>
                <div class="form-group">
                    <label for="InputFile">Avatar</label>
                    <input type="file" id="InputFile" name="avatar">
                    <p class="help-block">点击上传文件 上传头像</p>
                </div>
                <div class="form-group">
                    <span><b>爱好</b></span>
                    <br>
                    <label class="checkbox-inline">
                        <input type="checkbox" id="inlineCheckbox1" name="hobby" value="music"> music
                    </label>
                    <label class="checkbox-inline">
                        <input type="checkbox" id="inlineCheckbox2" name="hobby" value="rap"> rap
                    </label>
                    <label class="checkbox-inline">
                        <input type="checkbox" id="inlineCheckbox3" name="hobby" value="basketball"> basketball
                    </label>

                </div>

                <div class="form-group">
                    <span><b>性别</b></span>
                    <br>
                    <label class="radio-inline">
                        <input type="radio" name="gender" id="inlineRadio1" value="male"></label>
                    <label class="radio-inline">
                        <input type="radio" name="gender" id="inlineRadio2" value="female"></label>
                </div>
                <button type="submit" class="btn btn-default">Submit</button>
            </form>
        </div>
    </div>
</div>
</body>
</html>

此时在前端点击提交会报错,参考request对象post请求报错解决方法,将settings.py中MIDDLEWARE的csrf注释即可

image

注释之后在前端点击提交,后端打印相关请求数据

视图函数打印数据补充详细

def register(request, *args, **kwargs):
    # 如果是get请求,返回注册页面
    if request.method == 'GET':
        return render(request, 'register.html', locals())
    else:
        # 当前端请求是post提交数据时,先打印请求数据,重定向到登录路由,即刷新了页面
        print(request.POST)
        username = request.POST.get('username')
        password = request.POST.get('password')
        avatar = request.POST.get('avatar')
        hobby = request.POST.get('hobby')
        gender = request.POST.get('gender')
        print(f'username: {username}, password: {password}, avatar: {avatar, type(avatar)}, hobby: {hobby}, gender: {gender}')
        return redirect('register1')

image

文件上传后端接收到的是str类型,不是文件类型

image

因为此时是默认参数enctype="application/x-www-form-urlencoded",只能上传文件名称字符串,不能上传文件数据

3.3 上传文件内容

将以上前端form标签的参数加上enctype="multipart/form-data"

<form action="" method="post" enctype="multipart/form-data">

浏览器提交文件

image

avatar的内容由原来的字符串变为空

image

 文件数据不能用request.POST.get方法获取,要用request.FILES.get获取

app01的views.py中

通过键获取到具体的文件对象request.FILES.get('avatar')

def register(request, *args, **kwargs):
    # 如果是get请求,返回注册页面
    if request.method == 'GET':
        return render(request, 'register.html', locals())
    else:
        # 当前端请求是post提交数据时,先打印请求数据,重定向到登录路由,即刷新了页面
        print(request.POST)
        username = request.POST.get('username')
        password = request.POST.get('password')
        avatar = request.POST.get('avatar')
        hobby = request.POST.get('hobby')
        gender = request.POST.get('gender')
        print(
            f'username: {username}, password: {password}, avatar: {avatar, type(avatar)}, hobby: {hobby}, gender: {gender}')
        # 打印文件对象数据类型
        file_obj = request.FILES.get('avatar')
        print(file_obj, type(file_obj))

        # 读取文件数据  dir(file_obj) 查看对象所有属性和方法
        file_data = file_obj.read()
        base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # 根目录
        # 文件的保存路径为静态目录下的avatar目录,os模块可以往下拼两级目录
        file_save_dir = os.path.join(base_dir, 'static', 'avatar')
        os.makedirs(file_save_dir, exist_ok=True)
        # 使用print(dir(file_obj))可以查看文件对象的所有属性,在这里需要获取文件对象的名称
        # 将文件写入到目录
        with open(os.path.join(file_save_dir, file_obj.name), 'wb') as f1:
            f1.write(file_data)

        return redirect('register1')

打印文件对象类型

image

 浏览器提交图片文件时,文件可以提交到后端目录

image

4. request方法补充

方法 / 属性包含查询参数?包含脚本前缀 (SCRIPT_NAME)?用途
request.path ❌ 不包含 ✅ 包含 标准路径(带部署前缀)
request.path_info ❌ 不包含 ❌ 不包含 纯路由路径(不带前缀)
request.get_full_path() ✅ 包含 ✅ 包含 完整路径(带参数 + 前缀)
request.get_full_path_info() ✅ 包含 ❌ 不包含 纯路由完整路径(带参数)

关键概念:SCRIPT_NAME(脚本前缀)
这是区分它们的核心:当 Django 项目不是部署在根域名(比如 https://域名.com/),而是部署在子路径(比如 https://域名.com/myproject/)时:
SCRIPT_NAME = /myproject,这就是脚本前缀

第一组:不带查询参数(path vs path_info)
共同点:都不包含 URL 中的查询参数(?key=value)
区别:是否包含脚本前缀
示例
访问地址:https://xxx.com/myproject/article/1/?name=test
request.path = /myproject/article/1/
request.path_info = /article/1/

第二组:带查询参数(get_full_path vs get_full_path_info)
共同点:都包含 URL 中的查询参数区别:是否包含脚本前缀
示例
访问地址:https://xxx.com/myproject/article/1/?name=test
request.get_full_path() = /myproject/article/1/?name=test
request.get_full_path_info() = /article/1/?name=test

常用场景:
获取当前页面完整 URL(带参数) → 用 request.get_full_path()
反向路由 / 匹配 URL 规则 → 用 request.path_info(路由只识别纯路径)
获取纯路径(不含参数) → 用 request.path

5. FBV与CBV

5.1 概念

FBV(Function-Based View,基于函数的视图)和 CBV(Class-Based View,基于类的视图)是 Django框架处理 HTTP 请求的两种方式。

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

5.2 FBV

之前在应用的views.py中定义的函数就是FBV

5.3 CBV

[1] 概念

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

[2] 基本使用

在应用app01下的views.py中定义一个视图类

from django.views import View

# 创建一个视图类   这个类必须继承django的视图
class LoginView(View):
    # 以下函数get与post的名称不能修改,否则发起get/post请求时不能运行对应函数
    # 当浏览器发起get请求运行这个函数
    def get(self, request, *args, **kwargs):
        return HttpResponse("from get view")

    # 当浏览器发起post请求运行这个函数
    def post(self, request, *args, **kwargs):
        return HttpResponse("from post view")

app01的urls.py中定义路由

from django.urls import path
from app01.views import LoginView

urlpatterns = [
    path('login/', LoginView, name='login1')
]

应用路由添加到项目路由

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),
]

浏览器发起访问时报错

image

 更改视图类的路由定义方式

from django.urls import path
from app01.views import LoginView

urlpatterns = [
    path('login/', LoginView.as_view(), name='login1')
]

浏览器访问时不再报错

image

[3] CBV源码

1. 切入点:路由匹配

当浏览器发起访问请求,路由匹配成功先运行LoginView.as_view()

以路由的as_view作为切入点

path('login/', LoginView.as_view(), name='login1'),

类名.属性as_view,属性加了括号

  as_view可能是普通的静态方法

  as_view可能是绑定给类的方法

路由里点击as_view查看,as_view是绑定给类的方法

image

2.对象查找属性的顺序

  先从对象自身开始、再从产生对象的类、之后是各个基类

LoginView.as_view()查找as_view()

  先从自定义的LoginView类中查找

  没有再去基类View中查找 

3. 函数名加括号优先级最高

项目启动会先运行as_view方法

path('login/', LoginView.as_view(), name='login1'),

按照as_view对象的查找顺序,在基类View中查看源码

image

 源码返回了一个闭包函数view,因此路由可以理解如下

path('login/', LoginView.view, name='login1'),

FBV的路由第二个参数为函数名,以上CBV第二个参数也为函数名

因此,CBV与FBV的路由匹配本质是一样的

4. 当浏览器发起访问,login/路由匹配成功时,LoginView.view函数加括号运行

classonlymethod---绑定给类的方法

类可以直接调用绑定给类的方法,不需要额外传入cls参数,发起调用的类自动传给cls

as_view是绑定给类的方法,cls是自定义的类(这里为LoginView)

image

return self.dispatch(request, *args, **kwargs)中dispatch的查找顺序:

  先从对象self查找,没有则去产生self对象的自定义类中查找,再去自定义类的基类View中去查找

  (如果在LoginView中自定义dispatch方法,则覆盖系统的dispatch)

调用view函数的结果是调用dispatch函数

5. 调用dispatch

点击dispatch,查看源码

dispatch函数是CBV的核心

image

6. 总结 

项目启动,运行as_view()--->返回view函数--->路由匹配成功,LoginView.view函数加括号运行 ---> view 返回了 self.dispatch ---> dispatch 找到对应的 get / post 方法调用

posted @ 2026-03-01 22:56  pythondjango  阅读(21)  评论(0)    收藏  举报