Rest:一种面向资源的架构,这是一种概念。
RestFul:遵守了rest风格的应用。
Django采用FBV(function based view),DRF采用CBV (class based view)
两种应用广泛的接口规范:restful和RPC
序列化:将数据转化为特定的格式;反序列化:将特定格式的数据还原为我们需要的数据。
小知识
一个类中的方法加上@property注解后,不需要加括号就能调用该方法,该方法叫做属性方法
面向对象
python万物皆对象
class A(object):
name = "A的Name"
def __init__(self, name, age):
self.name = name
self.age = age
a = A("a传入的name", 23)在上面的代码示例中,a实例对象根据A类来创建了一片空间,**self**指的就是该实例化后的对象,而后执行__init__方法把self传入,将name和age进行初始化。即:
在实例化完毕后的对象中,只会初始化在创建它的类中出现的self.xxx的内容,不会出现其他任何内容。在访问a的属性时,会优先查找自己有的属性,如果没有会往上查找创建它的类中有没有,如果还没有继续往上访问父类中有没有。
在python中,万物皆对象,变量名和函数名都是对象,所以在调用实例对象的name()会直接报错,而不会去找类中有没有这个方法。
子类调用父类方法时不需要传入self,且父类中self就是子类(调用者)。可以理解为子类把父类的这个方法复制到自己的里面了。
class Father:
def __init__(self, name):
self.name = name
def say(self, age):
print(f'我是{self.name} {age}, {self.age}')
class son(Father):
age = 13
def foo(self):
self.say(12)
son1 = son('张三')
son1.foo()
-> 我是张三 12Django采用CBV模式的源码解读
Django使用CBV模式,通过方法名来区分请求方式。使用方式如下:
# view.py
class LoginView(View):
def get(self, request):
return HttpResponse('get')
def post(self, request):
return HttpResponse('post')
# urls.py
path('test/', views.LoginView.as_view()),源码解读:Django在启动时执行LoginView继承的父类View中的as_view()方法,在该方法中,存在有一个view()方法,在收到前端的请求后就执行view()方法。
在该方法中,做了一个分发的操作。通过dispatch方法,来区分出是什么类型的请求(post、get等)来执行不同的方法。非常简单的一种源码。
如何给**dispatch**方法加点自己的代码?
在LoginView(View)中,覆盖父类的方法dispatch,而后通过super().dispatch来把之前的代码放进来。
class LoginView(View):
def dispatch(self, request, *args, **kwargs):
print("添加代码")
return super().dispatch(request, *args, **kwargs)1. DRF的安装
pip install djangorestframework在settings.py中注册该组件
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'rest_framework'
]2. DRF的使用
采用CBV模式,但不继承Django自带的View,继承DRF的APIView, 这个类继承了View,且重新定义了request,使得其可以正常解析前端发来的Json数据。
class LoginView(APIView):
def get(self, request):
return HttpResponse('get')
def post(self, request):
data = request.data
print(data)
return HttpResponse('post')在重新定义的request中,可以通过request.data来获取前端post发送的json数据并解析为了python的字典。
该request的一些属性
.data返回解析后的请求体数据,包含了对POST、PUT、PATCH请求方式解析后的数据.query_params与request.get是一样的,只是名字变了。request._request获得Django自带的Request对象。
通过这种方式,我们可以轻松获取前端传来的数据。
2.1 序列化器进行序列化
序列化器用来辅助我们将Django查询来的数据序列化为json格式。
首先创建一个关于models中类的序列化类,
from rest_framework import serializers
class MySerializer(serializers.Serializer):
name = serializers.CharField()
age = serializers.IntegerField()通过这个序列化类,可以将数据库拿出来的数据进行序列化,注意需要使用DRF自带的Response
from rest_framework.response import Response
def post(self, request)
student = Student.objects.all()
serialzer = MySerializer(instance=students, many=True)
return Response(serialzer.data)在序列化类中,变量名是序列化后的json键名,也是在数据库中的字段名。如果你想要json返回的键名和数据库中的字段不一致,
names = serializers.CharField(source="name")2.2 进行反序列化
在反序列化之前,需要对前端传来的数据进行校验。
def post(self, request):
# 构建序列化器对象
serializer = BookDetailView(data=request.data)
try:
# is_valid方法进行校验,若校验不成功则直接报错
serializer.is_valid(raise_exception=True)
return Response(serializer.validated_data)
except Exception as e:
return Response(serializer.errors)在is_valid方法执行中,会创建两个列表,一个是**serializer.validated_data**,一个是**serializer.errors**,若数据校验成功则数据被添加到前者,若校验失败则把错误原因添加到后者。
2.3 添加、删除、修改、查找
- 添加数据实现
def post(self, request):
# 将传入的数据进行序列化并验证
serializer = BookDetailView(data=request.data)
try:
serializer.is_valid(raise_exception=True)
Book.objects.create(**serializer.validated_data)
return Response(serializer.validated_data)
except serializers.ValidationError as e:
return Response(serializer.errors)**serializer.data**是固定对于序列化对象实例化时传入的**instance****参数进行序列化。**在serializer.save()时,默认将自定义的create()方法返回的值作为了instance。
- 修改数据实现
def put(self, request, id):
# 将传入的数据进行序列化并验证
serializer = BookDetailView(data=request.data)
try:
serializer.is_valid(raise_exception=True)
Book.objects.filter(pk=id).update(**serializer.validated_data)
return Response(serializer.validated_data)
except:
return Response(serializer.errors)在上面的两种方法中,都是我们在put或者post方法中使用ORM操作了数据库添加数据,这不够优雅,还可以使用save()方法。
这是serializer.save()的源码,可以看到,该方法会通过检测instance是否有值来确定这是个新增还是修改。而后选择调用**create**还是**update**方法。这两个方法需要在序列化类中自己实现。
class BookDetailView(serializers.Serializer):
title = serializers.CharField(max_length=100)
author = serializers.CharField(max_length=100)
def create(self, validated_data):
return Book.objects.create(**validated_data)
def update(self, instance, validated_data):
return Book.objects.filter(pk=instance.pk).update(**validated_data)实现新增和修改,
def post(self, request):
# 进行数据校验
serializer = BookDetailView(data=request.data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.validated_data)
except serializers.ValidationError as e:
return Response(serializer.errors)
def put(self, request, id):
# 将传入的数据进行序列化并验证, instance设置为需要修改的原数据,data为修改后的数据
serializer = BookDetailView(instance=Book.objects.get(pk=id), data=request.data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.validated_data)
except:
return Response(serializer.errors)2.4 ModelSerializer
原来的serializer序列化类只能自己手动去写字段和方法,而ModelSerializer序列化类可以根据models模型类来自动生成字段和方法。
class BookDetailView(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['title'] # 设置需要哪些字段
exclude = ['author'] # 设置不需要哪些字段,这个设置与上面的设置冲突。2.5 GenericAPIView
在代码中使用一些不写死的属性来代替 序列化器和模型 ,用于提高代码的复用性,为下一步封装代码做准备。
在Post和Put中,
from rest_framework.generics import GenericAPIView
class LoginView(GenericAPIView):
# 指定模型
queryset = Book.objects.all()
# 指定序列化器
serializer_class = BookDetailView
def post(self, request):
# GenericAPIView借助queryset和serialzer_class来实例化serializer,传入的参数还是序列化器需要传入的参数
serializer = self.get_serializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.validated_data)
except serializers.ValidationError as e:
return Response(serializer.errors)
def put(self, request, pk):
# 将传入的数据进行序列化并验证
serializer = self.get_serializer(instance=self.get_object(), data=request.data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.validated_data)
except:
return Response(serializer.errors)需要注意的是,在put方法中,通过get_object()方法获取的需要修改的对象依赖于**pk**和有名传参的设置。pk这个参数名根据lookup_field = 'pk'来自定义设置,默认是pk,有名传参这样设置
re_path(r'test/(?P<pk>\d+)', views.LoginView.as_view())2.6 Minin混合类
在上面GenericAPIView类中,通过不写死属性来使得代码的复用性很强。所以DRF设置了各种类对重复的代码进行封装。
通过对各种类的多继承,来实现对一些方法的封装,以便在开发中直接使用
from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, ListModelMixin
class LoginView(GenericAPIView, CreateModelMixin, ListModelMixin):
queryset = Book.objects.all()
# 指定序列化器
serializer_class = BookDetailView
# 指定查询集
lookup_field = 'pk'
def get(self, request):
return self.list(self, request)
def post(self, request):
return self.create(self, request)ListModelMixin | list(self, request) | 提供查询列表功能(对应 HTTP 的 GET 方法),返回模型的多条数据 |
|---|---|---|
CreateModelMixin | create(self, request) | 提供创建资源功能(对应 HTTP 的 POST 方法),新增一条模型数据 |
RetrieveModelMixin | retrieve(self, request, pk) | 提供查询详情功能(对应 HTTP 的 GET 方法),返回单条模型数据(按主键查询) |
UpdateModelMixin | update(self, request, pk)partial_update(self, request, pk) | 提供更新资源功能(对应 HTTP 的 PUT/PATCH 方法),支持全量更新和部分更新 |
DestroyModelMixin | destroy(self, request, pk) | 提供删除资源功能(对应 HTTP 的 DELETE 方法),删除指定主键的模型数据 |
2.7 Viewset
只读viewset:
2.8 ModelViewSet
高度封装的一种类,自动实现增删改查和路由的建立。
示例:
class SubTodoViewSet(viewsets.ModelViewSet):
"""
子待办事项(SubTodo)的API视图集,提供CRUD功能
"""
serializer_class = SubTodoSerializer
filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
filterset_fields = ['status']
ordering_fields = ['created_at', 'priority', 'title', 'updated_at']
ordering = ['priority', '-created_at'] # 默认排序可以填入的参数:
可以自己重写方法来实现自定义逻辑:
2.8 认证
2.9 自定义返回json格式
序列化器 - Django REST 框架 - Django REST 框架
相关文档
- Django - Web 框架基础
- Python - Python 语言基础
- HTTP_204_状态码踩坑记录 - DRF 返回 204 状态码的坑
相关文档
- Django - Django Web 框架
- Python - Python 语言基础
- HTTP_204_状态码踩坑记录 - DRF 204 状态码问题