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
相关推荐
一点媛艺1 小时前
Kotlin函数由易到难
开发语言·python·kotlin
魔道不误砍柴功3 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
_.Switch3 小时前
高级Python自动化运维:容器安全与网络策略的深度解析
运维·网络·python·安全·自动化·devops
测开小菜鸟4 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
Ai 编码助手5 小时前
MySQL中distinct与group by之间的性能进行比较
数据库·mysql
陈燚_重生之又为程序员5 小时前
基于梧桐数据库的实时数据分析解决方案
数据库·数据挖掘·数据分析
caridle5 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
白云如幻5 小时前
MySQL排序查询
数据库·mysql
萧鼎5 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸5 小时前
【一些关于Python的信息和帮助】
开发语言·python