9-Django框架之视图层

HttpResponse

HttpResponse 是 Django 中用于创建 HTTP 响应对象的类,它允许你构建并返回服务器对客户端请求的数据和状态。

当需要直接返回纯文本数据(如 JSON 格式的数据)或者 HTML 页面时,可以使用 HttpResponse

正常返回纯文本

image-20240229183109074

返回一个字典类型时

image-20240229183011193

返回一个HTML标签

image-20240229183558867

返回json数据

image-20240229183811521

render

render 函数是 Django 的模板渲染机制的一部分,通常与模板引擎(如 Django 的默认模板引擎 - Jinja2 或者其它支持的模板引擎)一起使用。

当你想要返回带有动态内容的 HTML 页面时,可以使用 render 函数将请求的数据与预定义的 HTML 模板结合起来:

image-20240229184326852

redirect

redirect 函数用于实现网页间的重定向,即将用户从当前 URL 引导向另一个 URL。

它不返回任何内容,而是引发一个 HTTP Redirection 错误,迫使客户端发送一个新的请求到指定地址:

注意:不需要加request! 不然会和我一样在奇怪的地方卡住很久 QAQ

image-20240229192707064

img

JsonResponse

参数如果是一个字典或者列表类型,前端Ajax接收的就直接是一个对象了

先看一个案例,直接返回一个字典过去,数据直接把字典的键拼接到一起了,值直接没有了

image-20240229193723162

image-20240229193936963

用json试一下

image-20240229194621778

用JsonResponse试一下

from django.http import JsonResponse

image-20240229194805278

解决方案,添加一个参数,json_dumps_params={'ensure_ascii': False}就可以解决了,可以查看对应的源码,连引号也成中文了。

def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
             json_dumps_params=None, **kwargs):
    if safe and not isinstance(data, dict):
        raise TypeError(
            'In order to allow non-dict objects to be serialized set the '
            'safe parameter to False.'
        )
        if json_dumps_params is None:
            json_dumps_params = {}
            kwargs.setdefault('content_type', 'application/json')
            # 注意这个位置
            data = json.dumps(data, cls=encoder, **json_dumps_params)
            super().__init__(content=data, **kwargs)

image-20240229195939255

尝试返回一个字典

image-20240229200124319

JsonResponse在序列化字典以外的数据格式时,会有一个安全设置,我们将参数safe=False即可正常返回数据

image-20240229200234837

form表单文件上传下载

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

form表单想要上传文件类型的数据

  1. method 参数必须改为post

  2. enctype 参数必须指定成 form-data 类型

POST请求数据

image-20240229210108777

数据获取request.FILES

无法直接获取到数据,可以通过request.FILES去获取,得到的看起来像字符串,其实并不是字符串

if request.method == 'POST':
    file_obj = request.FILES
    print(file_obj)  # <MultiValueDict: {'一张图片': [<InMemoryUploadedFile: 三个女孩.jpg (image/jpeg)>]}>

image-20240229210622479

写入文件,遍历chunks()(官方建议)

def file_test(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('一张图片')
        with open(rf'E:\djangoProject\TestDemo\t1\static\t1\img\{file_obj}', 'wb') as file:
            for line in file_obj.chunks():
                # 差不多逐行写入
                file.write(line)

    return render(request, 't1/index.html')

image-20240229212213125

总结request对象方法

  • request.method:获取HTTP请求方法,可以是'GET'、'POST'、'PUT'、'DELETE'等。

  • request.GET: 获取通过 URL 查询参数传递的数据。通

    • 通过get取值
    request.GET <QueryDict: {'name': ['你好!'], 'password': ['123131'], 'hobby': ['music']}>
    
  • request.POST: 获取通过 POST 方法传递的表单数据,包含了请求中通过文件上传组件发送的所有文件,您可以使用文件的名字作为键来访问单个文件,例如request.FILES['file']

    • 通过get取值
    request.GET <QueryDict: {'name': ['你好!'], 'password': ['123131'], 'hobby': ['music']}>
    
  • request.FILES: 获取通过 POST 方法上传的文件数据。

    <MultiValueDict: {'一张图片': [<InMemoryUploadedFile: 三个女孩.jpg (image/jpeg)>]}>
    
  • request.COOKIES: 获取请求中的 cookie 数据。

  • request.session: 获取或设置与当前会话相关的数据。

  • request.path: 获取请求的路径部分。

    request.path /t1/show_data
    
  • equest.path_info:只能获取路由地址,无法获取查询字符串

    • 通常情况下,您可以使用 request.path 来获取丢弃域名后的路径,而使用 request.path_info 来获取原始的、未解析的路径。这在某些情况下非常有用,例如当您需要对URL进行一些自定义操作或路由处理时。

    •   /t1/show_data
      
  • request.get_full_path(): 获取完整的地址,即能获取到路由地址又能获取到完整的路由地址后面的参数

    •   /t1/show_data/?name=%E4%BD%A0%E5%A5%BD%EF%BC%81&password=123131&hobby=music
      
  • request.method: 获取请求的 HTTP 方法类型(GET、POST 等)。

  • request.META: 包含有关请求的元数据,如请求头信息等。

FBV与CBV引入

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

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

还可以把view写成基于类的。

FBV

FBV: function based view

就是平常在views.py中写的函数

# 一个简单的FBV
def tpl(request):
    data_list = []
    for index in range(10):
        url = "https://api.vvhan.com/api/love?type=json"
        data = requests.get(url, headers={'User-Agent': UserAgent().random}).json()
        data_list.append(data)
    return render(request, 'app1/tpl.html', {'data': data_list})

CBV

CBV: class based view

就是平常在views.py中写的类

Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:

  1. 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
  2. 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
from django.views import View
from django.shortcuts import render, reverse, redirect, HttpResponse
from . import models

# 注册视图
class Register(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'app1/register.html')

    def post(self, request, *args, **kwargs):
        # 获取用户名
        username = request.POST.get('username')
        # 查询数据库,如果数据库存在让用户继续注册,并打印错误信息在页面
        flag = models.UserInfo.objects.filter(username=username).filter()
        if flag:
            error = f'对不起,用户{username}已存在!'
            return render(request, 'app1/register.html', {'error': error})

        # 如果用户不存在,则把数据存入数据库
        password = request.POST.get('password')
        models.UserInfo.objects.create(username=username, password=password)
        
        # 跳转到登录视图
        login_url = reverse('app1:Login')
        return redirect(login_url)


# 登录视图
class Login(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'app1/login.html')

    def post(self, request, *args, **kwargs):
        # 登录视图直接查询数据库,验证是否登录过
        username = request.POST.get('username')
        password = request.POST.get('password')
        flag = models.UserInfo.objects.filter(username=username, password=password).filter()
        if flag:
            return HttpResponse('恭喜,登录成功!')
# 路由层
from . import views
from django.urls import path, re_path

app_name = "app1"

urlpatterns = [
    # 这里使用需要添加括号,剖析源码可以得知,得到的是内存地址,并不是真正的执
    path('register/', views.Register.as_view()),
    path('login/', views.Login.as_view(), name='Login')
]
<!-- register.html -->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册界面</title>
    <link rel="stylesheet" href="{% static 'app1/css/bootstrap-theme.css' %}">
    <link rel="stylesheet" href="{% static 'app1/css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'app1/css/style.css' %}">
    <script src="{% static 'app1/js/sweetalert.min.js' %}"></script>
</head>
<body>
    <form method="post">
        {% csrf_token %}
        <p>输入账号:<input type="text" id="username" name="username" placeholder="此处输入账号" required>
            <span class="error-msg"> {{ error }}</span>
        </p>
        <p>输入密码:<input type="password" name="password" placeholder="此处输入密码" required></p>
        <button type="submit" id="submit-msg" class="btn btn-success">提交</button>
        <button type="reset" class="btn btn-success">重置</button>
    </form>
</body>
</html>
{# login.html #}
{% include 'app1/register.html' %}

image-20240301192404353

  • 选择使用 FBV 还是 CBV 取决于具体的需求和个人偏好。
  • FBV 相对简单直观,适合编写简单的视图逻辑;
  • 而 CBV 可以通过继承和重写类来实现代码复用和可扩展性,适用于复杂的视图处理场景。
  • 在实际开发中,可以根据需求选择适合的方式来编写视图处理函数或类。

给视图添加装饰器

给FBV添加装饰器

FBV同普通函数一样,略过不谈

给CBV添加装饰器

方法1,正常使用函数的语法糖装饰器即可

import time
from functools import wraps

def timer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        t1 = time.time()
        res = func(*args, **kwargs)
        print('总耗时', time.time()-t1)  # 总耗时 2.0102639198303223
        return res
    return inner


class UseLess(View):
    @timer
    def get(self, request):
        time.sleep(2)
        return HttpResponse('ok')

方法2,通过databases模块

import time
from dataclasses import dataclass

@dataclass
class Timer:
    f: callable

    def __call__(self, *args, **kwargs):
        t1 = time.time()
        res = self.f(self, *args, **kwargs)
        print('总耗时', time.time()-t1)  # 总耗时 2.0062646865844727
        return res


class UseLess(View):
    @Timer
    def get(self, request):
        time.sleep(2)
        return HttpResponse('ok')

方法3,使用django自带的装饰器method_decorator

import time
from functools import wraps
# 使用前先导入
from django.utils.decorators import method_decorator

# 函数装饰器不需要做任何修改
def timer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        t1 = time.time()
        res = func(*args, **kwargs)
        print('总耗时', time.time()-t1)  # 总耗时 2.00778865814209
        return res
    return inner


class UseLess(View):
    # 注意这里
    @method_decorator(timer)
    def get(self, request):
        time.sleep(2)
        return HttpResponse('ok')

方法4,重写dispatch (了解即可)

使用CBV时要注意,请求过来后会先执行dispatch()这个方法
如果需要批量对具体的请求处理方法,如get,post等做一些操作的时候,这里我们可以手动改写dispatch方法,这个dispatch方法就和在FBV上加装饰器的效果一样。

class Login(View):
     
    def dispatch(self, request, *args, **kwargs):
        print('before')
        obj = super(Login,self).dispatch(request, *args, **kwargs)
        print('after')
        return obj
 
    def get(self,request):
        return render(request,'login.html')
 
    def post(self,request):
        print(request.POST.get('user'))
        return HttpResponse('Login.post')
posted @ 2024-03-23 00:49  小满三岁啦  阅读(1)  评论(0编辑  收藏  举报