Django框架之视图层
3651 字
18 分钟
Django框架之视图层
- 1纯手撸Web框架
- 2Python框架初识
- 3Django框架初识
- 4Django框架安装
- 5Django框架入门之小白必会三板斧
- 6Django便捷函数shortcuts
- 7Django框架入门之静态文件
- 8Django的静态文件
- 9Django框架入门之request对象
- 10Django框架入门之ORM操作
- 11Django框架之生命周期流程图
- 12Django框架之路由层
- 13Django框架之视图层本文
- 14Django框架之模板层
- 15Django框架之模型层
- 16图书管理系统
- 17Ajax基础操作
- 18Ajax进阶操作
- 19JS确认插件补充
- 20Django框架之序列化组件serializers
- 21Django框架之批量插入数据
- 22Django框架补充之分页器推导
- 23Django框架之Form组件
- 24Django框架Form组件之常用字段及插件
- 25Django框架Form组件之渲染HTML代码
- 26Django框架Form组件之渲染错误信息
- 27Django框架Form组件之钩子函数(Hook)
- 28Django框架Form组件之源码
- 29Django框架Form组件之模型表单ModelForm
- 30Django表单API详解
- 31Django框架之Cookie和Session
- 32Django框架之会话session补充
- 33Django框架之CBV添加装饰器的三种方式
- 34Django框架之中间件引入
- 35Django框架之中间件思想
- 36Django框架之csrf跨站请求
- 37Django框架之auth模块
- 38Django框架之settings源码
- 39Django框架之settings配置
- 40Django框架之settings核心配置项
- 41RBAC权限系统
- 42前端基础之URL编码
【一】三板斧
【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 renderfrom .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对应的视图(通常是登录页面)。
- 如果用户已登录,则继续执行原视图中的其他逻辑。
【二】JsonResponse
- 路由配置
from django.contrib import adminfrom django.urls import pathfrom app01 import views
urlpatterns = [ path('admin/', admin.site.urls), path('json_data/', views.json_data),]【1】json模块序列化
import jsonfrom django.shortcuts import HttpResponse
def json_data(request): user_dict = { "username": "dream你好", 'age': 18, 'password': 123123, }
# 先转成JSON格式字符串 # ensure_ascii:中文不编码 json_str = json.dumps(user_dict, ensure_ascii=False)
# 然后将该字符串返回 return HttpResponse(json_str)【2】JsonResponse对象
(1)问题引入
- 返回的页面展示的数据中,中文字符被强制编码了,如何解决?
from django.http import JsonResponse- 源码分析
class JsonResponse(HttpResponse):
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(JsonResponse, self).__init__(content=data, **kwargs)- 通过分析源码我们可以发现数据表被序列化的位置
- 其中 有一个参数 **json_dumps_params
- 这个参数意思是将数据打散传入到前面的方法中
(2)解决方案
def ab_json(request): user_dict = { "username": "dream你好", 'age': 18, 'password': 123123, } return JsonResponse(user_dict, json_dumps_params={'ensure_ascii': False})- 将我们需要的参数以字典的形式传进去
(3)其他数据类型序列化
- 我们不仅有字典格式的数据可以被序列化也有其他操作
- 例如列表
def ab_json(request): user_dict = { "username": "dream你好", 'age': 18, 'password': 123123, } user_list = [11, 22, 33, 44, 55, 66, 77, 88, 99]
return JsonResponse(user_list, json_dumps_params={'ensure_ascii': False})- 我们在访问数据的时候会提示下面的错误
In order to allow non-dict objects to be serialized set the safe parameter to False.- 我们按照提示加上需要的参数
def ab_json(request): user_dict = { "username": "dream你好", 'age': 18, 'password': 123123, } user_list = [11, 22, 33, 44, 55, 66, 77, 88, 99]
return JsonResponse(user_list, safe=False, json_dumps_params={'ensure_ascii': False})- JsonResponse在序列化字典以外的数据格式时,会有一个安全设置,我们将参数修改即可修改数据
【二】form表单文件上传下载
form表单上传数据以及后端如何获取
【0】form知识回顾
<form action="" method="post" enctype="multipart/form-data"></form>- form表单想要上传文件类型的数据
- method 参数必须改为post
- enctype 参数必须指定成 form-data 类型
【1】数据准备
- 路由urls.py
from django.contrib import adminfrom django.urls import pathfrom app01 import views
urlpatterns = [ path('admin/', admin.site.urls), # form 表单上传 下载文件 path('file_upload/', views.file_upload),]- 前端file.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head><body><form action="" method="post" enctype="multipart/form-data" class="form form-control"> <p>username:<input type="text" name="username" class="form-control"></p> <p>file:<input type="file" name="file" class="form-control"></p> <input type="submit"></form></body></html>【2】数据处理
(1)POST请求数据
- csrf报错,记得注释掉中间件
def file_upload(request): if request.method == 'POST': # 只能获取到普通的文本数据,无法获取到文件数据 print(request.POST) return render(request, 'file.html')<QueryDict: {'username': ['dream']}>- 通过这种方式,我们只能获取到我们输入的文本数据,而拿不到我们想要的文件数据
(2)文件数据获取
def file_upload(request): if request.method == 'POST': # 获取文件数据 print(request.FILES) # <MultiValueDict: {'file': [<InMemoryUploadedFile: img.png (image/png)>]}>
# 提取文件数据 - 文件对象 file_obj = request.FILES.get('file') # 提取文件名字 file_obj.name with open(file_obj.name, 'wb') as f: # 逐行读取文件数据 # 官方推荐 加上 chunks 方法 等价于 一行行获取 for line in file_obj.chunks(): f.write(line) return render(request, 'file.html')<MultiValueDict: {'file': [<InMemoryUploadedFile: img.png (image/png)>]}>【三】总结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
只能获取到路由地址,无法获取到参数
- request.path:该属性表示请求URL中的路径部分。
- 它包含在域名之后,在任何查询参数之前。
- 例如,如果请求的URL是”http://example.com/foo/bar/“,那么request.path将为”/foo/bar/”。
【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()
即能获取到路由地址又能获取到完整的路由地址后面的参数
- request.get_full_path():该方法返回请求URL的完整路径,包括路径部分和任何查询参数。
- 当您需要将完整URL作为字符串使用时,这个方法非常有用。
- 例如,如果请求的URL是”http://example.com/foo/bar/?page=2”,request.get_full_path()将返回”/foo/bar/?page=2”。
【四】FBV与CBV引入
- 视图函数既可以是函数也可以是类
- 我们之前写过的都是基于函数的view,就叫FBV。
- 还可以把view写成基于类的。
【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")- MBV和CBV各有各的特点,都有应用
- CBV特点
- 能够直接根据请求方式的不同直接匹配到对应的方法执行
【3】详解
- FBV(Function-Based Views)与 CBV(Class-Based Views)是 Django 框架中用于处理视图(Views)的两种不同的编程方式。
(1)Function-Based Views(FBV):
- FBV 是 Django 最早支持的一种视图编程方式。
- 使用函数作为视图的处理逻辑。函数参数通常是 request 对象,表示用户发送的 HTTP 请求。
- 函数内部可以根据请求方法、参数、数据等进行相应的处理,并返回 HttpResponse 对象作为响应。
- 示例代码:
from django.http import HttpResponse
def my_view(request): # 处理逻辑 if request.method == 'GET': # 处理 GET 请求 return HttpResponse('Hello, GET Request!') elif request.method == 'POST': # 处理 POST 请求 return HttpResponse('Hello, POST Request!')(2)Class-Based Views(CBV):
- CBV 是 Django 提供的另一种视图编程方式,引入了面向对象的概念。
- 使用类来表示视图,并使用类中的方法来处理不同类型的HTTP请求。
- Django 提供了许多内置的 CBV 类,可以方便地扩展和重写这些类来实现自定义功能。
- 示例代码:
from django.http import HttpResponsefrom django.views import View
class MyView(View): def get(self, request): # 处理 GET 请求 return HttpResponse('Hello, GET Request!')
def post(self, request): # 处理 POST 请求 return HttpResponse('Hello, POST Request!')(3)小结
- 选择使用 FBV 还是 CBV 取决于具体的需求和个人偏好。
- FBV 相对简单直观,适合编写简单的视图逻辑;
- 而 CBV 可以通过继承和重写类来实现代码复用和可扩展性,适用于复杂的视图处理场景。
- 在实际开发中,可以根据需求选择适合的方式来编写视图处理函数或类。
【五】CBV源码剖析
【1】as_view 方法
(1)路由 对应 函数内存地址
path('login/', views.MyLogin.as_view()),方法/函数名 加 括号 执行优先级最高
- 可能的原因
- 要么是被@staicmethod方法修饰的静态方法
- 要么是被@classmethod方法修饰的类方法
- 查看源码
- 绑定给类的静态方法,将类作为第一个参数传进去
@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("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (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))
def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs 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- view 这是一个闭包函数
- 返回值是这个闭包函数的内存地址
- 在启动Django项目时,就会立刻执行as_view方法
- 即
path('login/', views.view()),- 方法类似FBV方法
总结:CBV和FBV在路由匹配上本质上是一样的,都是路由 对应 函数内存地址
(2)view 方法剖析
def view(request, *args, **kwargs): # 参数 cls 就是我们自己写的类 self = cls(**initkwargs) # 等价于 self = MyLogin(**initkwargs) ---- 产生一个我们自己写的类的对象 if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get # 给我们的对象赋值属性 self.request = request self.args = args self.kwargs = kwargs ''' 在看python源码的时候,一定要时刻注意面向对象属性方法查找顺序 先从对象自己找 再去产生对象的类里面找 之后再去父类找 总结:在看源码时,如果遇到 self.x 操作时,一定要时刻注意当前这个self到底是谁 ''' return self.dispatch(request, *args, **kwargs)http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
# CBV 的 精髓 !!! def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. # 获取当前请求的小写格式 然后比对当前请求方式是否合法 # 以 get 请求为例 if request.method.lower() in self.http_method_names: ''' 反射:通过字符串来操作对象的属性或方法 ''' # self 是我们自己写的类产生的对象 # handler = getattr(自己写的类产生的对象,'get',当找不到get属性或方法时就会用到第三个参数) # 因为我们在自己的类里面重写了 get 方法 ,所以这里的 handler 就变成了我们自己写的 get方法 # handler = 我们自己写的类里面的get方法 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed
# 自动调用 get 方法 return handler(request, *args, **kwargs)(3)小结
path('login/', views.MyLogin.as_view()),- 当我们启动Django项目时
- 会自动触发路由中的方法,调用 as_view 方法并自执行
- 在执行后我们查看 as_view 方法的源码 发现
- 在依次给我们的对象赋值后,最终返回了一个自执行的 dispatch 方法
- 于是我们又去查看了 dispatch 方法
- 在 dispatch 内部 ,先是将请求方式转换并进行校验
- 然后开始校验需要调用的方法的调用位置,校验成功并拿到需要执行的方法执行
- 在自己写的类中如果有相关的方法,会首先调用我们重写的类方法,并返回执行结果
- 如果自己的类里面没有该方法 会去自己的父类中调用 父类的方法
- 如果父类 以及 基类 都找不到则报错,抛出异常
- 如果自己的类里面没有该方法 会去自己的父类中调用 父类的方法
【六】给视图加装饰器
【1】使用装饰器装饰FBV
- FBV本身就是一个函数,所以和给普通的函数加装饰器无差:
def wrapper(func): def inner(*args, **kwargs): start_time = time.time() ret = func(*args, **kwargs) end_time = time.time() print("used:", end_time-start_time) return ret return inner
# FBV版添加班级@wrapperdef 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
- 类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用于类中的方法 ,我们需要先将其转换为方法装饰器。
- Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。
# CBV版添加班级from django.views import Viewfrom django.utils.decorators import method_decorator
def wrapper(func): def inner(*args, **kwargs): start_time = time.time() ret = func(*args, **kwargs) end_time = time.time() print("used:", end_time-start_time) return ret return inner
class AddClass(View):
@method_decorator(wrapper) def get(self, request): return render(request, "add_class.html")
def post(self, request): class_name = request.POST.get("class_name") models.Classes.objects.create(name=class_name) return redirect("/class_list/")【3】CBV的拓展
- 使用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')支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
相关文章智能推荐
1
C 语言发展史
编程语言了解 C 语言从 BCPL、B 语言到现代标准 C 的发展历程、核心特点与应用领域。
2
C 语言开发环境的安装与配置
编程语言在 Windows、Linux 与 macOS 上安装 C 工具链,配置编辑器,并编译运行第一个 C 程序。
3
C 语言初识:程序运行机制与基本结构
编程语言认识 C 程序从源代码到运行的完整流程,以及基础语法和程序的基本结构。
4
C 语言基础:注释、关键字、变量与数据类型
编程语言学习 C 语言的注释、关键字、标识符、常量、变量和基本数据类型等基础知识。
5
C 语言数据类型
编程语言理解 C 语言的数据类型体系,包括基本类型、整数类型、浮点类型和逻辑类型。
随机文章随机推荐














