Published in:2024-10-24 |

drf-day2

1. 知识回顾

1
2
3
4
5
6
7
8
9
10
1. 前后端分离的架构
后端负责数据的提供 前端完成页面的展示以及数据的渲染 二者通过ajax进行交互
2. 接口API
3. RESTFul架构
遵从了REST规范
4. Django类视图
FBV: function base view 函数视图
CBV: class base view 类视图
5. DRF的视图开发
APIView继承了Django的View类

2. Request模块

主要功能: 用来获取请求中所包含的各种参数

入口: request = self.initialize_request(request, *args, **kwargs)

Request类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Request:
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
assert isinstance(request, HttpRequest), (
.format(request.__class__.__module__, request.__class__.__name__)
)

# request是django原生的request对象, 赋值给DRF视图类作为一个属性 _request
# self就是当前视图类
self._request = request
self.parsers = parsers or ()
self.authenticators = authenticators or ()
self.negotiator = negotiator or self._default_negotiator()
self.parser_context = parser_context
self._data = Empty
self._files = Empty
self._full_data = Empty
self._content_type = Empty
self._stream = Empty

DRF获取参数的方式

  1. GET请求可以通过request.GET以及request.query_params来获取参数
  2. POST请求有多种传递参数的形式,request.data可以获取多种类型的参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class StudentAPIView(APIView):

def get(self, request, *args, **kwargs):
print("get Success")
# WSGI request
print(request._request.GET.get("email")) # 不推荐
# restframework.views.Request
print(request.GET.get("email"))
# 通过DRF扩展的方式来获取参数
print(request.query_params.get("pwd")) # DRF扩展的获取参数的方式

return Response("GET OK")

def post(self, request, *args, **kwargs):
print("post Success")
print(request._request.POST.get("email"))
print(request.POST.get("email"))
# 可以获取前端传递各种类型的参数 DRF扩展的 兼容性最强
print(request.data)

return Response("POST OK")

3. 渲染模块 render

渲染器: 决定了你的数据以何种形式返回到前端

源码流程

  1. 源码入口: self.finalize_response(request, response, *args, **kwargs)
  2. 找到neg = self.perform_content_negotiation(request, force=True)来返回渲染器
  3. 获取当前视图所配置的渲染器: renderers = self.get_renderers()
  4. 通过return [renderer() for renderer in self.renderer_classes]找到配置的渲染器
  5. 在DRF的setting模块中定义默认的渲染器DEFAULT_RENDERER_CLASSES

使用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# DRF的全局配置
REST_FRAMEWORK = {
# DRF渲染器默认配置
'DEFAULT_RENDERER_CLASSES': [
# json格式的渲染器
'rest_framework.renderers.JSONRenderer',
# 浏览器渲染
'rest_framework.renderers.BrowsableAPIRenderer',
# 'rest_framework.renderers.TemplateHTMLRenderer',
],
}


# 局部使用
class StudentAPIView(APIView):
# 为某个视图单独指定渲染器 局部使用
# 局部配置的优先级高于全局配置
renderer_classes = (BrowsableAPIRenderer,)

def get(self, request, *args, **kwargs):
print("get Success")

4. DRF解析模块 parser

解析模块: 指定当前视图可以接受什么类型的参数 form-data www-url-encode json

源码解析

  1. 源码入口: self.initialize_request(request, *args, **kwargs)
  2. 获取解析器: parsers=self.get_parsers(),
  3. 获取配置的解析器: return [parser() for parser in self.parser_classes]

使用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# DRF的全局配置
REST_FRAMEWORK = {
......
# DRF默认的解析器的配置
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser', # 解析json数据
'rest_framework.parsers.FormParser', # 解析不带文件的form数据
'rest_framework.parsers.MultiPartParser' # 解析带文件的form数据
],
}

# 局部配置
class StudentAPIView(APIView):
# 为某个视图单独指定解析器 局部使用
# 局部配置的优先级高于全局配置
parser_classes = [JSONParser]

def get(self, request, *args, **kwargs):
print("get Success")

5. DRF的异常处理

异常模块主要是处理在各个方法执行过程中所发生的问题

异常源码

  1. 异常处理源码入口: response = self.handle_exception(exc)
  2. 获取处理异常的方法: exception_handler = self.get_exception_handler()
  3. 返回处理异常的方法: return self.settings.EXCEPTION_HANDLER
  4. 异常处理的逻辑在: rest_framework.views.exception_handler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def handle_exception(self, exc):
"""
Handle any exception that occurs, by returning an appropriate response,
or re-raising the error.
"""
# 判断异常是否属于某个异常实例
if isinstance(exc, (exceptions.NotAuthenticated,
exceptions.AuthenticationFailed)):
# WWW-Authenticate header for 401 responses, else coerce to 403
auth_header = self.get_authenticate_header(self.request)

if auth_header:
exc.auth_header = auth_header
else:
exc.status_code = status.HTTP_403_FORBIDDEN

# 获取处理异常的方法 handler
exception_handler = self.get_exception_handler()

context = self.get_exception_handler_context()
# 异常处理的结果 判断exception_handler方法处理的结果是否有值
# 有值: 代表异常已经被DRF处理了 值为None: 代表DRF无法处理此异常
response = exception_handler(exc, context)

# 根据上个方法处理的异常信息判断异常是否已经被处理
if response is None:
self.raise_uncaught_exception(exc)

response.exception = True
# 如果response有值 则代表异常已经被处理 返回异常信息
return response

自定义异常的处理

对于DRF无法处理的某些异常,我们可以通过自定义的方式完成处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from rest_framework.response import Response
from rest_framework.views import exception_handler as drf_exception_handler


def exception_handler(exc, context):
error = "%s %s %s" % (context["view"], context["request"].method, exc)
print(error)

# 先让DRF处理异常 根据异常处理的返回值来判断异常是否被处理
response = drf_exception_handler(exc, context)

# 如果返回值为None 代表DRF无法处理此异常 需要自定义处理
if response is None:
return Response({"error_message": "上帝请稍等,程序猿正在加紧处理中~"})

# 如果response不为空 说明异常已经被处理了
return response

1
2
3
4
5
# 总结
1. 异常处理是通过`self.get_exception_handler()`获取异常处理的方法
2. 通过`return self.settings.EXCEPTION_HANDLER`获取DRF默认的处理异常的方法
3. 自定义异常时可以先让drf处理已知的异常,DRF无法处理的异常由我们自定义处理
4. 可以通过`exception_handler`来判断异常是否已经被处理

6. Response模块

Response类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Response(SimpleTemplateResponse):
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
super().__init__(None, status=status)

if isinstance(data, Serializer):
msg = (
'You passed a Serializer instance as data, but '
'probably meant to pass serialized `.data` or '
'`.error`. representation.'
)
raise AssertionError(msg)

self.data = data
self.template_name = template_name
self.exception = exception
self.content_type = content_type

"""
data=None, 响应回去的数据
status=None, 响应的状态码
template_name=None 渲染的模板地址 不处理
headers=None, 响应头
exception=False, 是否有异常
content_type=None 数据格式
"""

7. DRF 序列化组件[重点]

DRF常用的序列化器类: Serializer(偏底层)、ModelSerializer(常用)、ListSerializer(做群体操作)

模型设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 静态资源目录
MEDIA_ROOT = os.path.join(BASE_DIR, "media/")

from django.views.static import serve
from django.conf import settings
re_path(r'^media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT}),

class Employee(models.Model):
gender_choices = (
(0, "male"),
(1, "female"),
(2, "other"),
)

username = models.CharField(max_length=100)
password = models.CharField(max_length=64)
gender = models.SmallIntegerField(choices=gender_choices, default=0)
phone = models.CharField(max_length=11, null=True, blank=True)
pic = models.ImageField(upload_to="pic/", default="pic/1.jpg")

class Meta:
db_table = "bz_employee"
verbose_name = "员工"
verbose_name_plural = verbose_name

def __str__(self):
return self.username

Serializer初始

  • 序列化器的定义
1
2
3
4
5
6
7
8
class EmployeeSerializer(serializers.Serializer):
"""
定义序列化器类: 需要为每一个model编写对应的序列化器类
"""
username = serializers.CharField()
password = serializers.CharField()
gender = serializers.IntegerField()
pic = serializers.ImageField()
  • 序列化器的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class EmployeeAPIView(APIView):

def get(self, request, *args, **kwargs):

emp_id = kwargs.get("id")

if emp_id:
# 查询单个
emp_obj = Employee.objects.get(pk=emp_id)

# 使用序列化器完成对象的序列化
# .data 将序列化器中的数据打包成字典返回
employee_serializer = EmployeeSerializer(emp_obj).data

return Response({
"status": 200,
"message": "查询单个员工成功",
"results": employee_serializer
})

else:
employee_objects_all = Employee.objects.all()

# 在序列化多个对象时 需要制定属性many=True
emp_data = EmployeeSerializer(employee_objects_all, many=True).data
print(emp_data)

return Response({
"status": 200,
"message": "查询所有员工成功",
"results": emp_data
})
1
2
3
4
5
# 总结
1. 需要为每个模型指定一个单独的序列化器类
2. 在为前端返回数据时,需要响应序列化后的 .data的值
3. 在序列化器中的字段必须与模型的字段保持一致,否则无法序列化, 如果某个字段不想序列化时,可以不写
4. 序列化多个对象时,需要指定many=True

对字段进行自定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class EmployeeSerializer(serializers.Serializer):
"""
定义序列化器类: 需要为每一个model编写对应的序列化器类
"""
username = serializers.CharField()
password = serializers.CharField()
# gender = serializers.IntegerField()
# pic = serializers.ImageField()

# 自定义字段 使用SerializerMethodField来完成自定义
aaa = serializers.SerializerMethodField()

# 为自定义字段提供值的方法
# 自定义字段的属性名随意 但是为字段提供值的方法必须是 get_字段名
# self是当前序列化器对象 obj是当前被序列化的对象
def get_aaa(self, obj):
return "aaa"

# 自定义性别
gender = serializers.SerializerMethodField()

def get_gender(self, obj):
# gender 值是choices类型 get_字段名_display直接访问值
print(obj.get_gender_display())
return obj.get_gender_display()

pic = serializers.SerializerMethodField()

def get_pic(self, obj):
print(obj.pic)
# http://127.0.0.1:8000/media/pic/3.jpg
# print("http://127.0.0.1:8000/" + settings.MEDIA_URL + str(obj.pic))
return "%s%s%s" % ("http://127.0.0.1:8000/", settings.MEDIA_URL, str(obj.pic))
1
2
3
4
# 总结
1. 字段类型文档: https://www.django-rest-framework.org/api-guide/fields/
2. 序列化: 数据从数据库查出,将数据传递给序列化器进行转化成Response可以识别的类型,通过data属性可以获取到序列化后的数据,返回到前端
3. 反序列化: 数据从前端传入到视图,通过序列化器保存到数据库

反序列化

  • 反序列化器定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class EmployeeDeSerializer(serializers.Serializer):
"""
反序列化: 将前端提交的数据保存到数据库
1. 需要前端提供哪些字段
2. 对字段进行安全校验
3. 有没有字段需要额外的校验
反序列化不需要自定义字段
"""

# 添加校验规则
username = serializers.CharField(
max_length=3,
min_length=2,
error_messages={
"max_length": "长度太长了",
"min_length": "长度太短了",
}
)
password = serializers.CharField()
phone = serializers.CharField()

# 如果想要完成对象的新增 必须重写create方法
# self是序列化器对象 validated_data需要保存的数据
def create(self, validated_data):
print(self)
print(validated_data)
return Employee.objects.create(**validated_data)
  • 反序列化视图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def post(self, request, *args, **kwargs):

# 获取前端传递的参数
request_data = request.data

# 前端传递的数据进行入库时 需要判断数据的格式是否合法
if not isinstance(request_data, dict) or request_data == {}:
return Response({
"status": 400,
"message": "参数有误",
})

# 使用序列化器完成数据库的反序列化
# 在数据进行反序列化的时候需要指定关键字 data
serializer = EmployeeDeSerializer(data=request_data)

# 需要对反序列化的数据进行校验 通过is_valid() 方法来对传递过来的参数进行校验 校验合法时才会返回True
if serializer.is_valid():
# 调用save()方法进行数据的保存 必须重写create()方法
emp_ser = serializer.save()
print(emp_ser)
return Response({
"status": 200,
"message": "员工添加成功",
"results": EmployeeSerializer(emp_ser).data
})
else:

return Response({
"status": 400,
"message": "员工添加失败",
# 保存失败的信息会包含在 .errors中
"results": serializer.errors
})
  1. 反序列化时从前端获取的参数必须以关键字的形式EmployeeDeSerializer(data=request_data)传递到序列化器类
  2. 通过序列化类的is_valid对传递到序列化器的数据进行校验. 在此时才会调用序列化器为每个字段定义好的反序列化规则. 如果用过校验返回True, 失败返回False
  3. 保存失败的错误信息会包含在serializer.errors
  4. 通过serializer.save()保存对象时需要重写create()方法

作业

1
2
3
4
5
1. 掌握各个模块的作用以及使用方式  掌握源码流程
request render parser 异常处理 Response
2. 掌握DRF序列化器的使用 [重点重点]
使用序列化器完成 教师表的 查询单个 查询所有 新增单个
删除(可选)
Prev:
Next: