一、认识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

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐