挑战全网最全之django REST framework(DRF)教程
一、认识DRF
1、restful简介
在前后端分离的应用模式中,我们通常将后端开发的每个视图都称为一个接口,或者API,前端通过访问接口来对数据进行增删改查。
restful是一种后端API接口规范
1.1 http动词
对于资源的具体操作类型,由HTTP动词表示。
常用的HTTP动词有下面四个(括号里是对应的SQL命令):
- GET(SELECT):从服务器取出资源(一项或多项)。
- POST(CREATE):在服务器新建一个资源。
- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
- DELETE(DELETE):从服务器删除资源。
还有三个不常用的HTTP动词:
- PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
- HEAD:获取资源的元数据。
- OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的
使用例子:
GET /zoos:列出所有动物园
POST /zoos:新建一个动物园(上传文件)
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
1.2 过滤信息(Filtering)
例子:
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件
1.3 状态码
- 200 OK - [GET]:服务器成功返回用户请求的数据
- 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
- 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
- 204 NO CONTENT - [DELETE]:用户删除数据成功。
- 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
- 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
- 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
- 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
- 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
- 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
- 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
- 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
1.4 错误处理
如果状态码是4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可
{
error: "Invalid API key"
}
1.5 返回结果
针对不同操作,服务器向用户返回的结果应该符合以下规范。服务器返回的数据格式,应该尽量使用JSON
- GET /collection:返回资源对象的列表(数组)
- GET /collection/resource:返回单个资源对象
- POST /collection:返回新生成的资源对象
- PUT /collection/resource:返回完整的资源对象
- PATCH /collection/resource:返回完整的资源对象
- DELETE /collection/resource:返回一个空文档
2、DRF简介
Django REST framework 框架是一个用于构建Web API 的强大而又灵活的工具
特点:
- 提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;
- 提供了丰富的类视图、Mixin扩展类,简化视图的编写;
- 多种身份认证和权限认证方式的支持;
- 直观的 API web 界面;
二、搭建
1、下载包
pip install djangorestframework
2、添加rest_framework应用
INSTALLED_APPS = [
...
'rest_framework',
]
三、序列化与反序列化
序列化过程:响应的python对象转化为响应报文的json字节流
1、拿到对象
student = Student.oject.all()
2、将对象交给序列化类
serializer = StudentSerializer(student)
3、字典转json字节流
JSONRenderer().render(serializer.data())
反序列化过程:请求body的json字节流转化为python对象
1、json字节流转为字典
data = JSONParser().parse(BytesIO(content))
2、通过serializer将字典转对象
serializer = StudentSerializer(data=data)
3、校验对象数据
serializer.is_valid() #校验通过返回True,失败返回False
4、保存对象
serializer.save()
下面将会以该模型展开教程:
学生和班级是一对多
班级和老师是多对多
班级和班主任是一对一
from django.db import models
# Create your models here.
class Student(models.Model):
student_id = models.AutoField(primary_key=True, verbose_name="学生编号")
student_name = models.CharField(max_length=8, verbose_name="学生姓名")
student_gender = models.BooleanField(
choices=([0, "male"], [1, "female"]), default=0, verbose_name="学生性别")
student_class = models.ForeignKey(
to="Classes", verbose_name="所属班级", on_delete=models.CASCADE) # 一个班级多个学生
def __str__(self):
return self.student_name
class Meta:
db_table = ''
managed = True
verbose_name = 'Student'
verbose_name_plural = 'Students'
class Classes(models.Model):
class_id = models.AutoField(primary_key=True, verbose_name="班级编号")
class_name = models.CharField(max_length=8, verbose_name="班级名称")
class_teacher = models.OneToOneField(
to="Teacher", verbose_name="班主任", on_delete=models.CASCADE) # 一个班级只有一个班主任
def __str__(self):
return self.class_name
class Meta:
db_table = ''
managed = True
verbose_name = 'Classes'
verbose_name_plural = 'Classess'
class Teacher(models.Model):
teacher_id = models.AutoField(primary_key=True, verbose_name="教师编号")
teacher_name = models.CharField(max_length=8, verbose_name="教师姓名")
teacher_class = models.ManyToManyField(
to="Classes", verbose_name="任教班级") # 一个班级中可有多个老师,一个老师也可以在多个班级中任教
def __str__(self):
return self.teacher_name
class Meta:
db_table = ''
managed = True
verbose_name = 'Teacher'
verbose_name_plural = 'Teachers'
1、创建序列类
序列类的作用如下:
- 序列化时,可选择序列化的模型表字段
- 反序列化时,可选择对数据验证的规则
Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer
2、序列化
from .models import Student
from rest_framework import serializers
class StudentSerializer(serializers.Serializer):
student_id = serializers.CharField()
student_name = serializers.CharField()
student_gender = serializers.BooleanField()
student_class = serializers.PrimaryKeyRelatedField(many=False, queryset=Teacher.objects.all())
def to_representation(self, instance):
class = instance.student_class
ret = super(StudentSerializer, self).to_representation(instance)
ret["student_class"] = {
"id": class.id,
"name": class.name
}
return ret
序列化的过程中,会根据类中定义的序列化类的字段进行序列化输出。不定义则不会输出
如果默认的序列化输出无法满足我们的需求,那么可以重写 to_representation() 改变序列化的输出。如上所示,显示班级的指定信息。
如果使用PrimaryKeyRelatedField 此字段将被序列化为关联对象的主键
3、反序列化
当进行反序列化时,可以在序列类中指定一些参数,对将要反序列化写入模型表的字段进行检查
from .models import Student
from rest_framework import serializers
class StudentSerializer(serializers.Serializer):
GENDER_CHOICES = (
(0, 'female'),
(1, 'male')
)
student_id = serializers.CharField(read_only=True)
student_name = serializers.CharField(required=True, max_length=15, label="学生姓名", help_text="学生姓名")
student_gender = serializers.ChoiceField(choices=GENDER_CHOICES, label='性别', required=False)
student_class = serializers.PrimaryKeyRelatedField(many=False, queryset=Idc.objects.all())
3.1 create、update方法
在进行反序列化时,必须在序列类中覆写create()方法以及update()方法。如果不进行覆写,则会抛出异常。
执行serializer.save()方法时就会进行判断执行create或者update。反序列化时提交的数据如果不带id字段那么自动调用create方法,带id字段则调用update方法。
其中create()方法针对的是新增,而update()方法针对的是更新。
例子
def create(self, validated_data):
instance = Student.objects.create(**validated_data)
return instance # 这里返回的信息会返回到序列类对象的data属性中
def update(self, instance, validated_data):
instance.student_name = validated_data.get("name", instance.student_name)
instance.student_genter = validated_data.get("genter", instance.student_genter)
instance.student_class = validated_data.get("class", instance.student_class)
instance.save()
return instance
3.2 反序列化钩子
反序列化过程中对数据进行校验
-
validate_字段名 :局部钩子,对指定字段的校验
-
validate :是全局钩子,对象的校验
如下是局部钩子的使用示例,因为页面提交过来的数据关于一对多中的班级字段是字符串,所以我们需要将字符串变为模型表对象,方便后面的创建以及更新。
def validate_student_class(self, data):
# data是提交过来的这一个字段的数据
class_obj = Classes.objects.filter(class_name=data).first()
if not class_obj:
raise exceptions.ValidationError("班级不存在")
data = class_obj # 将字符串替换为对象
return data
全局钩子使用也是一样。如下,验证学生名和班级名是否相同,如果相同则抛出异常
def validate(self, validate_data):
student_name = validate_data.get("student_name")
class_obj = validate_data.get("student_class") # 由于局部钩子中,这里被替换成了对象,所以我们拿到对象不能直接作比较
if student_name == class_obj.class_name:
raise exceptions.ValidationError("学生名不能和班级名相同")
return validate_data
3.3 to_internal_value()
允许改变我们反序列化的输出
def to_internal_value(self, data):
"""
反序列化第一步:拿到的是提交过来的原始数据: QueryDict => request.GET, request.POST
"""
print(data)
return super(StudentSerializer, self).to_internal_value(data)
四、视图
4.1 Request 与 Response
4.1.1 Request
REST framework 传入视图的request对象是REST framework提供的扩展了HttpRequest类的Request类的对象。提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典对象保存到Request对象中
request.data 返回解析之后的请求体数据
request.query_params 与Django标准的request.GET作用相同
4.1.2 Response
REST framework提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。
构造方式 Response(data, status=None, template_name=None, headers=None, content_type=None)
常用属性:
1).data
传给response对象的序列化后,但尚未render处理的数据
2).status_code
状态码的数字
3).content
经过render处理后的响应数据
4.2 视图
4.2.1 APIView
rest_framework.views.APIView
APIView是REST framework提供的所有视图的基类
在APIView中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。
###
urlpatterns = [
url("^books/$", views.IdcList.as_view(), name="book-list"),
url("^books/(?P<pk>[0-9]+)/$", views.IdcDetail.as_view(), name="book_detail")
]
###
from rest_framework.views import APIView
from rest_framework.response import Response
from django.http import Http404
class BookList(APIView):
def get(self, request, format=None):
queryset = Book.objects.all()
serializer = BookSerializer(queryset, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)
class BookDetail(APIView):
//该方法自定义
def get_object(self, pk):
try:
return Book.objects.get(pk=pk)
except Idc.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
Book = self.get_object(pk)
serializer = BookSerializer(Book)
return Response(serializer.data)
def put(self, request, pk, format=None):
Book = self.get_object(pk)
serializer = BookSerializer(idc, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_404_NOT_FOUND)
def delete(self, request, pk, format=None):
Book = self.get_object(pk)
Book.delete()
return HttpResponse(status=status.HTTP_204_NO_CONTENT)
4.2.2 GenericAPIView
rest_framework.generics.GenericAPIView
继承自APIVIew,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可搭配一个或多个Mixin扩展类
提供的关于序列化器使用的属性与方法
属性:
- serializer_class 指明视图使用的序列化器
方法:
- get_serializer_class(self) 返回序列化器类
- get_serializer(self, args, *kwargs) 返回序列化器对象
提供的关于数据库查询的属性与方法
属性:
- queryset 指明使用的数据查询集
方法:
- get_queryset(self) 返回视图使用的查询集
- get_object(self) 返回视图所需的模型类数据对象
eg:
# url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
class BookDetailView(GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request, pk):
book = self.get_object() # get_object()方法根据pk参数查找queryset中的数据对象
serializer = self.get_serializer(book)
return Response(serializer.data)
4.2.3 五个扩展类
提供了几种后端视图(对数据资源进行曾删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。
这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法
4.2.3.1 ListModelMixin
列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。
该Mixin的list方法会对数据进行过滤和分页。
eg:
from rest_framework.mixins import ListModelMixin
class BookListView(ListModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request):
return self.list(request)
4.2.3.2 CreateModelMixin
创建视图扩展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。
如果序列化器对前端发送的数据验证失败,返回400错误。
4.2.3.3 RetrieveModelMixin
详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。
如果存在,返回200, 否则返回404。
eg:
class BookDetailView(RetrieveModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request, pk):
return self.retrieve(request)
4.2.3.4 UpdateModelMixin
更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。
同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。
成功返回200,序列化器校验数据失败时,返回400错误。
eg:
4.2.3.5 DestroyModelMixin
删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。
成功返回204,不存在返回404。
4.2.4 八个可用子类视图
1) CreateAPIView
提供 post 方法
继承自: GenericAPIView、CreateModelMixin
2)ListAPIView
提供 get 方法
继承自:GenericAPIView、ListModelMixin
3)RetrieveAPIView
提供 get 方法
继承自: GenericAPIView、RetrieveModelMixin
4)DestoryAPIView
提供 delete 方法
继承自:GenericAPIView、DestoryModelMixin
5)UpdateAPIView
提供 put 和 patch 方法
继承自:GenericAPIView、UpdateModelMixin
6)RetrieveUpdateAPIView
提供 get、put、patch方法
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
7)RetrieveUpdateDestoryAPIView
提供 get、put、patch、delete方法
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
4.3 视图集
4.3.1 ViewSet
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:
list() 提供一组数据
retrieve() 提供单个数据
create() 创建数据
update() 保存数据
destory() 删除数据
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等
视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。如:
class BookInfoViewSet(viewsets.ViewSet):
def list(self, request):
books = BookInfo.objects.all()
serializer = BookInfoSerializer(books, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
try:
books = BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = BookInfoSerializer(books)
return Response(serializer.data)
urlpatterns = [
url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
]
4.3.2 GenericViewSet
使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView。
GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{‘get’:‘list’})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。
4.3.3 ModelViewSet
继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
4.3.4 ReadOnlyModelViewSet
继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。
4.3.5 视图集中定义附加action动作
在视图集中,除了上述默认的方法动作外,还可以添加自定义动作。
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def latest(self, request):
"""
返回最新的图书信息
"""
book = BookInfo.objects.latest('id')
serializer = self.get_serializer(book)
return Response(serializer.data)
def read(self, request, pk):
"""
修改图书的阅读量数据
"""
book = self.get_object()
book.bread = request.data.get('bread')
book.save()
serializer = self.get_serializer(book)
return Response(serializer.data)```
--------------------------------------------------------
urlpatterns = [
url(r'^books/$', views.BookInfoViewSet.as_view({'get': 'list'})),
url(r'^books/latest/$', views.BookInfoViewSet.as_view({'get': 'latest'})),
url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'})),
url(r'^books/(?P<pk>\d+)/read/$', views.BookInfoViewSet.as_view({'put': 'read'})),
]
4.3.6 路由Routers
对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息。
1) 创建router对象,并注册视图集,例如
from rest_framework import routers
router = routers.SimpleRouter()
router.register(r'books', BookInfoViewSet, base_name='book')
如上述代码会形成的路由如下:
^books/$ name: book-list
^books/{pk}/$ name: book-detail
-----------------------------------------------------
2)添加路由数据
urlpatterns = [
...
url(r'^', include(router.urls))
]
在视图集中,如果想要让Router自动帮助我们为自定义的动作生成路由信息,需要使用rest_framework.decorators.action装饰器。
以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。
action装饰器可以接收两个参数:
- methods: 声明该action对应的请求方式,列表传递
- detail: 声明该action的路径是否与单一资源对应,及是否是xxx//action方法名/
True 表示路径格式是xxx//action方法名/
False 表示路径格式是xxx/action方法名/
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
# detail为False 表示路径名格式应该为 books/latest/
@action(methods=['get'], detail=False)
def latest(self, request):
"""
返回最新的图书信息
"""
...
# detail为True,表示路径名格式应该为 books/{pk}/read/
@action(methods=['put'], detail=True)
def read(self, request, pk):
"""
修改图书的阅读量数据
"""
...
---------------------------------------------------
由路由器自动为此视图集自定义action方法形成的路由会是如下内容:
^books/latest/$ name: book-latest
^books/{pk}/read/$ name: book-read
五、权限管理
1. 认证
认证Authentication。指定对用户进行鉴权的方式,获取request.user。
一共有三种认证方式:
-
BasicAuthentication:HTTP基础认证。
前端将用户名和密码以Base64编码的形式,设置在Authorization HTTP头中,后端用以认证用户。 -
TokenAuthentication:基于Token的认证。
使用Authorization HTTP头里面的Token认证用户。 -
SessionAuthentication:使用Djnago的会话后台来认证。
使用Django Session Backend认证用户。
自定义认证方式:
DRF还允许自定义认证方式,只需要继承BaseAuthentication类,并实现.authenticate(self, request)方法。
2.权限控制
权限控制Permissions。针对Django资源、用户类别进行权限控制。
常见的四4种权限控制的类别:
- AllowAny 允许所有用户
- IsAuthenticated 仅通过认证的用户
- IsAdminUser 仅管理员用户
- IsAuthenticatedOrReadOnly 认证的用户可以完全操作,否则只能get读取
自定义权限:
如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部
-
.has_permission(self, request, view)
是否可以访问视图, view表示当前视图对象 -
.has_object_permission(self, request, view, obj)
是否可以访问数据对象, view表示当前视图, obj为数据对
六、其他
1、分页
https://blog.csdn.net/wu0che28/article/details/81460735
更多推荐
所有评论(0)