Django视图层

【一】Django框架之生命周期流程图

【二】三板斧

【1】HttpEesponse

(1)介绍

  • HttpResponse是Django中用于创建HTTP响应的类
  • 当直接要返回纯文本数据(如Json格式数据)或者HTML页面时,都可以使用HttpResponse

(2)属性方法

  • HttpResponse.set_cookie(name, value...):设置响应中的cookie
  • .delete_cookie(name...):删除响应中的cookie
  • .get(value, default=None):获取响应头中指定的值
  • .set_header(key, value):设置响应头中的键值对

(3)示例

python 复制代码
from django.http import HttpResponse

def my_view(request):
    content = "Hello, world!"  # 响应内容
    response = HttpResponse(content, status=200, content_type="text/plain")
    return response
  • content:响应的内容,可以是字符串、字节流或可迭代对象。
  • status:响应的状态码,默认为 200(OK)。
  • content_type:响应的内容类型,默认为 "text/html"。
python 复制代码
from django.http import HttpResponse

def my_view(request):
    content = "<html><body><h1>Hello, world!</h1></body></html>"
    response = HttpResponse(content, content_type="text/html")
    return response

【2】render

(1)介绍

  • render是Django框架中用于渲染模板并生成最终的响应
  • 当需要返回带有动态信息的HTML页面时,可以使用render函数将请求的数据与定义的模板HTML文件结合起来
  • 看源码可以发现render函数的底层还是HttpResponse

(2)基本语法

python 复制代码
from django.shortcuts import render

def my_view(request):
    # 处理逻辑
    return render(request, 'template_name.html', context)
  • request:请求对象,用于获取客户端发送的请求信息
  • template_name:模板文件的名称,可以是字符转或包含多个模板文件的列表
  • context:上下文变量,是一个字典,用于将数据传递给模板

(3)示例

  • 视图函数
python 复制代码
from django.shortcuts import render

def index(request):
    context = {'name': "bruce", 'age': 18}
    return render(request, 'index.html', context)
  • 前端模板文件
html 复制代码
<p>name: {{ name }}</p>
<p>age: {{ age }}</p>

【3】redict

(1)介绍

  • redict函数用于实现网页间的重定向
    • 将用户当前的URL引导至另一个URL
  • 这个函数没有返回值,而是发送一个HTTP Redirection错误,迫使客户端发送一个新的请求地址
  • 查看源码发现底层还是用的HttpResponse

(2)示例

  • 视图函数
    • 触发这个函数自动跳转至index
python 复制代码
from django.shortcuts import redirect

def index(request):
    return HttpRespinse("index")

def my_view(request):
    # 处理逻辑
    return redirect('index')

【三】JsonResponse

【1】回顾Json模块

(1)序列化

  • 使用.dumps函数可以将Python格式转换为Json格式的字符串
    • 其中有个参数ensure_ascii=False
    • 可以取消自动转码
python 复制代码
user_dict = {user_dict = {"username": "秦始皇", "age": 100}
with open("01.json", "w", encoding="utf8") as fp:
    json.dump(user_dict, fp=fp)
# {"username": "\u79e6\u59cb\u7687", "age": 100}
}

【2】JsonResponse

(1)序列化

  • 和json一样
    • 可以序列化python格式为json字符串格式并发送到浏览器上
python 复制代码
def index(request):
    data = {'name': '秦始皇', 'age': 100}
    return JsonResponse(data)

(2)取消转义

  • 中文可能发生转移,那么接下来研究怎么取消转义

  • 查看源码

python 复制代码
    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)
  • 分析源码发现使用的还是jason

  • 那么只要想办法把ensure_ascii传入即可

  • 最终发现参数json_dumps_param可以将ensure_ascii=false传进去

  • 所以修改语句

python 复制代码
return JsonResponse(user_dict, json_dumps_params={'ensure_ascii': False})

(3)其他数据类型序列化

  • 列表序列化
python 复制代码
def index(request):
    user_list = [11, 212, 32]
    return JsonResponse(user_list)
  • 报错
python 复制代码
In order to allow non-dict objects to be serialized set the safe parameter to False.
  • 意思为:为了允许序列化非dict对象,请将safe参数设置为False。
  • 那么按照要求改
python 复制代码
def index(request):
    user_list = [11, 212, 32]
    return JsonResponse(user_list, safe=False)

【四】form表单上传下载文件

【1】数据准备

  • 前端登录界面
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    {% load static %}
    <script src="{% static 'js/jquery-3.5.1.min.js' %}"></script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>

</head>
<body>
<div class="container">
    <div class="row">
        <h1 class="text-center">注册界面</h1>
        <div class="col-md-6 col-md-offset-3">

            <form action="" method="post">
                <p>用户名:<input class="form-control" type="text" name="username" placeholder="username"></p>
                <p>密码:<input class="form-control" type="password" name="password" placeholder="password"></p>
                <p>头像:<input type="file" name="head_jpg"></p>
                <p><input type="submit" class="btn btn-block btn-success"></p>
            </form>
        </div>
    </div>
</div>
</body>
</html>

【2】获取数据

(1)尝试POST直接获取

  • 视图函数
python 复制代码
def register(request):
    if request.method == "POST":
        data = request.POST
        print(data)
        username = data.get("username")
        password = data.get("password")
        head_image = data.get("head_image")
        print(head_image, type(head_image))
    return render(request, 'register.html')
  • 结果为图片名称字符串,没有图片数据
python 复制代码
<QueryDict: {'username': [''], 'password': [''], 'head_image': ['53efb9d8079c6e4f904ed602f5c1e4e.png']}>
53efb9d8079c6e4f904ed602f5c1e4e.png <class 'str'>

(2)requset.FILES

  • 视图函数
python 复制代码
def register(request):
    if request.method == "POST":
        files_data = request.FILES
        print(files_data)
        head_image = files_data.get("head_image")
        print(head_image, type(head_image))
    return render(request, 'register.html')
  • 结果为空
python 复制代码
<MultiValueDict: {}>
None <class 'NoneType'>

(3)修改form表单

  • 想要获取文件还需要将表单进行修改
python 复制代码
<form action="" method="post" enctype="multipart/form-data">

</form>
  • 刷新再次运行视图函数
python 复制代码
<MultiValueDict: {'head_image': [<InMemoryUploadedFile: 53efb9d8079c6e4f904ed602f5c1e4e.png (image/png)>]}>
53efb9d8079c6e4f904ed602f5c1e4e.png <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
  • 成功拿到了数据

(4)保存数据

  • 获取文件名

    • 直接.name就可以拿到文件名
  • 获取文件数据

    • 如果只是

    python 复制代码
    with open(保存文件名, mode="wb") as fp:
    	fp.write(head_image)
    • 将会报错

    python 复制代码
    a bytes-like object is required, not 'InMemoryUploadedFile'
    • 我们需要逐行读取,并且推荐保存文件写法如下
python 复制代码
def register(request):
    if request.method == "POST":
        files_data = request.FILES
        # print(files_data)
        head_image = files_data.get("head_image")
        # print(head_image, type(head_image))
        head_image_name = head_image.name
        print(head_image_name)
        with open(head_image_name, mode="wb") as fp:
            for line in head_image.chunks():
                fp.write(line)

    return render(request, 'register.html')

【五】request对象方法

【1】.method

  • 返回客户端发起请求的HTTP方法
  • 例如:GET、POST
  • 注意,返回的值是大写的字符串

【2】.POST

  • 获得页面通过POST请求发送的数据
  • 类似于字典的对象,可以通过键值对获取值
  • 通常由前端的form表单发送的

【3】.GET

  • 获得通过GET请求发送的数据
  • 也是类似于字典的对象,通过键值对取值
  • get请求的参数通常是附加在URL的后买你,以?分割URL和数据,数据和数据之间通过&符号链接

【4】.FILES

  • 获得上传的文件
  • 也是类似于字典的对象
  • 读取保存文件要通过一行一行的读取加上chunck()

【5】.path

  • 只能获取路由地址,无法获取到参数
    • 不包含域名和查询参数
  • 例如:http://example.com/foo/bar/
    • 将得到/foo/bar/

【6】.path_info

  • 和path基本一样,相比于path
    • 他保留了URL路径中的任何编码、特殊字符、斜杠等信息
  • 通常情况下,可以使用 request.path 来获取丢弃域名后的路径,而使用 request.path_info 来获取原始的、未解析的路径。这在某些情况下非常有用,例如当需要对URL进行一些自定义操作或路由处理时。

【7】.get_full_path

  • 该方法返回请求URL的完整路径,包括路径部分和任何查询参数。
  • 例如,如果请求的URL是http://example.com/foo/bar/?page=2
    • 将返回/foo/bar/?page=2

【六】FBV和CBV

  • 视图层不仅可以写成函数,还可以写成类

【1】FBV

(1)介绍

  • 前面写的视图层,用的全是FBV
  • FBV(Function Based Views)是Django最早支持的一种视图编写方式
  • 简单直观,适合编写简单逻辑代码

(2)示例

  • 视图层
python 复制代码
from django.http import render

def login(request, *args, **kwargs):
    if request.method = "POST":
        # 处理post请求逻辑代码
        return render(request, 'login.html')
    # 处理get请求逻辑代码
    return render(request, 'login.html')
  • 路由层
python 复制代码
from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.login, name='login'),
]

【2】CBV

(1)介绍

  • CBV(Class Based Views)是Django提供的另一种视图编写方式,通过面向对象编写
  • 通过继承和重写类实现代码复用和可扩展性
  • 适用于复杂的试图处理场景

(2)示例

  • 视图层
python 复制代码
from django.http import render
from django.views import View  
class Login(View):
	def get(self, request, *args, **kwargs):
        # 处理get请求逻辑代码
        return render(request, 'login.html')
    
    def post(self, request, *args, **kwargs):
        # 处理post请求逻辑代码
        return render(request, 'login.html')
  • 路由层
python 复制代码
from django.urls import path
from . import views

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

【3】CBV源码分析

python 复制代码
class View:

	# 定义支持的HTTP方法名
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    # 类View的初始化方法
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

	# 理解为类方法
    @classonlymethod
    def as_view(cls, **initkwargs):
        # cls是我们自己创建的类
        # as_view如果有参数对其进行检验方法名或者属性名书否冲突
        # 如果有前面定义的HTTP列表中的键值那就抛出异常
        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))
		
        # 这是一个闭包函数,对外部的cls有引用
        def view(request, *args, **kwargs):
            # cls是自己创建的类,实例化了一个对象self
            self = cls(**initkwargs)
  			# 调用setup方法进行
            self.setup(request, *args, **kwargs)
            # 检查对象是否有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__
                )
            return self.dispatch(request, *args, **kwargs)
        
        # 对View类的一些属性修改
        view.view_class = cls
        view.view_initkwargs = initkwargs
        update_wrapper(view, cls, updated=())
        update_wrapper(view, cls.dispatch, assigned=())
        # 返回闭包函数的地址
        return view

    # 初始化类试图的属性
    def setup(self, request, *args, **kwargs):
		# 如果定义了类方法且没有定义head方法,那么将head方法复制为get方法
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
	 
    # 检查对象实例此时的method是不是HTTP支持的方法名
    def dispatch(self, request, *args, **kwargs):
        # 因为method获取的方法名是大写的,所以需要转换为小去列表中比较
		# 支持的方法名,就将方法取出复制给handler
        # 不支持就触发第三个参数这是HTTP不支持的请求
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)
	
    # 返回警告内容为405状态码和HTTP允许的列表
    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())
	
    # 处理OPtions请求
    def options(self, request, *args, **kwargs):
        """Handle responding to requests for the OPTIONS HTTP verb."""
        response = HttpResponse()
        response.headers['Allow'] = ', '.join(self._allowed_methods())
        response.headers['Content-Length'] = '0'
        return response
    
	# HTTP支持的列表转大写返回
    def _allowed_methods(self):
        return [m.upper() for m in self.http_method_names if hasattr(self, m)]

【七】视图装饰器

【1】FBV装饰器

  • FBV本身是一个函数,那就直接用以前学习的知识就可以

  • 视图层

python 复制代码
from django.shortcuts import render

# 装饰器
def outer(func):
    def inner(*args, **kwargs):
        # 函数执行前逻辑处理
        res = func(*args, **kwargs)
		# 函数执行后逻辑处理
        return res
    return inner

@outer
def index(request, *args, **kwargs):
    # 逻辑处理
    return render(request, 'index.html')

【2】CBV装饰器

(1)函数方法直接加

  • 和FBV的添加方式一样
  • 不在展示

(2)使用method_decorator

  • Django提供了method_decorator装饰器
  • 也可以用在FBV上应用
  • CBV的应用有两种方法
    1. 直接 用来类里面对应的方法上
    2. 类外 使用,需要指定方法名
python 复制代码
from django.shortcuts import render, HttpResponse, redirect
from django.utils.decorators import method_decorator

# 装饰器
def outer(func):
    def inner(*args, **kwargs):
        # 函数执行前逻辑处理
        start = time.time()
        res = func(*args, **kwargs)
        # 函数执行后逻辑处理
        print(f"用时:{time.time() - start}")
        return res

    return inner
@method_decorator(outer, 'post')
@method_decorator(outer, 'get')
class Login(View):
    # @method_decorator(outer)
    def get(self, request):
        time.sleep(2)
        return render(request, 'login.html')

    # @method_decorator(outer)
    def post(self, request):
        time.sleep(2)
        return render(request, 'login.html')

(3)重写dispatch方法

  • 此方法将所有的类方法都加上了装饰器
  • 当然装饰器也可以写在dispath方法外面
    • dispatch直接加上装饰器
python 复制代码
from django.shortcuts import render, HttpResponse, redirect
from django.utils.decorators import method_decorator

class Login(View):
    def get(self, request):
        time.sleep(2)
        return render(request, 'login.html')

    def post(self, request):
        time.sleep(2)
        return render(request, 'login.html')
	
    # @method_decorator(outer)
    def dispatch(self, request, *args, **kwargs):
        # 函数执行前逻辑处理
        start = time.time()
        obj = super().dispatch(request, *args, **kwargs)
        # 函数执行后逻辑处理
        print(f"用时:{time.time() - start}")
        return obj
相关推荐
Databend9 分钟前
Databend 亮相 RustChinaConf 2025,分享基于 Rust 构建商业化数仓平台的探索
数据库
得物技术1 小时前
破解gh-ost变更导致MySQL表膨胀之谜|得物技术
数据库·后端·mysql
数据智能老司机4 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机5 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机5 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机5 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i5 小时前
drf初步梳理
python·django
每日AI新事件5 小时前
python的异步函数
python
Raymond运维6 小时前
MariaDB源码编译安装(二)
运维·数据库·mariadb
沢田纲吉6 小时前
🗄️ MySQL 表操作全面指南
数据库·后端·mysql