【一】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就可以拿到文件名
-
获取文件数据
-
如果只是
pythonwith open(保存文件名, mode="wb") as fp: fp.write(head_image)
-
将会报错
pythona 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的应用有两种方法
- 直接 用来类里面对应的方法上
- 再类外 使用,需要指定方法名
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