REST framework框架的基本组件

restful规范

1.根据method不同,进行不同操作
2.面向资源编程
3.体现版本
4.体现是API
5.最好用https
6.响应式设置状态码
7.条件 ?id=1
8.返回值
9.返回错误信息
10.Hypermedia API

REST framework框架

1.路由
2.视图
3.权限
4.认证
5.访问频率限制
6.序列化
7.分页
8.解析器
9.渲染器
10.版本

我对 django rest framework框架的认识!

- 路由,

- 可以通过as_view传参数,根据请求方式不同执行相应的方法
- 可以在url中设置一个结尾,类似于: .json

- 视图,

- 帮助开发者提供了一些类,并在类中提供了多个方法以供我们使用。

- 版本,

- 在url中设置version参数,用户请求时候传入参数。在request.version中获取版本,根据版本不同做不同处理

- 认证,

- 写一个类并注册到认证类,在类的的authticate方法中编写认证逻辑。
- 认证成功(user,auth)
- raise AuthticateFaild(....)
- None

- 权限

- 写一个类并注册到权限类,在类的的has_permission方法中编写认证逻辑。
- True
- False

- 频率限制

- 写一个类并注册到频率类,在类的的 allow_request/wait 方法中编写认证逻辑。

allow_request
- True
- False 如果返回False,那么就要执行wait

- 解析器,

- 根据ContentType请求头,选择不同解析器对 请求体中的数据进行解析。

POST /index/ http1.1. host:11.11.11.11 Content-Type:url-formendo.... user=alex&age=123
POST /index/ http1.1. host:11.11.11.11 Content-Type:application/json {....}

- 分页

- 对从数据库中获取到的数据进行分页处理: SQL -> limit offset
- 根据页码:http://www.luffycity.com/api/v1/student/?page=1&size=10
- 根据索引:http://www.luffycity.com/api/v1/student/?offset=60&limit=10
- 根据加密:http://www.luffycity.com/api/v1/student/?page=erd8

页码越大速度越慢,为什么以及如何解决?
原因:页码越大向后需要扫描的行数越多,因为每次都是从0开始扫描。
解决:
- 限制显示的页数
- 记录当前页数据ID最大值和最小值,再次分页时,根据ID现行筛选,然后再分页。

- 序列化

- 对queryset序列化以及对请求数据格式校验。

- 渲染器

- 根据URL中传入的后缀,决定在数据如何渲染到到页面上。

视图三部曲

使用混合(mixins)

 1 from rest_framework.views import APIView
 2 from rest_framework.response import Response
 3 from .models import *
 4 from django.shortcuts import HttpResponse
 5 from django.core import serializers
 6 
 7 
 8 from rest_framework import serializers
 9 
10 
11 class BookSerializers(serializers.ModelSerializer):
12       class Meta:
13           model=Book
14           fields="__all__"
15           #depth=1
16 
17 
18 class PublshSerializers(serializers.ModelSerializer):
19 
20       class Meta:
21           model=Publish
22           fields="__all__"
23           depth=1
24 
25 
26 class BookViewSet(APIView):
27 
28     def get(self,request,*args,**kwargs):
29         book_list=Book.objects.all()
30         bs=BookSerializers(book_list,many=True,context={'request': request})
31         return Response(bs.data)
32 
33 
34     def post(self,request,*args,**kwargs):
35         print(request.data)
36         bs=BookSerializers(data=request.data,many=False)
37         if bs.is_valid():
38             print(bs.validated_data)
39             bs.save()
40             return Response(bs.data)
41         else:
42             return HttpResponse(bs.errors)
43 
44 
45 class BookDetailViewSet(APIView):
46 
47     def get(self,request,pk):
48         book_obj=Book.objects.filter(pk=pk).first()
49         bs=BookSerializers(book_obj,context={'request': request})
50         return Response(bs.data)
51 
52     def put(self,request,pk):
53         book_obj=Book.objects.filter(pk=pk).first()
54         bs=BookSerializers(book_obj,data=request.data,context={'request': request})
55         if bs.is_valid():
56             bs.save()
57             return Response(bs.data)
58         else:
59             return HttpResponse(bs.errors)
60 
61 
62 class PublishViewSet(APIView):
63 
64     def get(self,request,*args,**kwargs):
65         publish_list=Publish.objects.all()
66         bs=PublshSerializers(publish_list,many=True,context={'request': request})
67         return Response(bs.data)
68 
69 
70     def post(self,request,*args,**kwargs):
71 
72         bs=PublshSerializers(data=request.data,many=False)
73         if bs.is_valid():
74             # print(bs.validated_data)
75             bs.save()
76             return Response(bs.data)
77         else:
78             return HttpResponse(bs.errors)
79 
80 
81 class PublishDetailViewSet(APIView):
82 
83     def get(self,request,pk):
84 
85         publish_obj=Publish.objects.filter(pk=pk).first()
86         bs=PublshSerializers(publish_obj,context={'request': request})
87         return Response(bs.data)
88 
89     def put(self,request,pk):
90         publish_obj=Publish.objects.filter(pk=pk).first()
91         bs=PublshSerializers(publish_obj,data=request.data,context={'request': request})
92         if bs.is_valid():
93             bs.save()
94             return Response(bs.data)
95         else:
96             return HttpResponse(bs.errors)
使用混合mixins

mixin类编写视图

 1 from rest_framework import mixins
 2 from rest_framework import generics
 3 
 4 class BookViewSet(mixins.ListModelMixin,
 5                   mixins.CreateModelMixin,
 6                   generics.GenericAPIView):
 7 
 8     queryset = Book.objects.all()
 9     serializer_class = BookSerializers
10 
11     def get(self, request, *args, **kwargs):
12         return self.list(request, *args, **kwargs)
13 
14     def post(self, request, *args, **kwargs):
15         return self.create(request, *args, **kwargs)
16 
17 
18 
19 class BookDetailViewSet(mixins.RetrieveModelMixin,
20                     mixins.UpdateModelMixin,
21                     mixins.DestroyModelMixin,
22                     generics.GenericAPIView):
23     queryset = Book.objects.all()
24     serializer_class = BookSerializers
25 
26     def get(self, request, *args, **kwargs):
27         return self.retrieve(request, *args, **kwargs)
28 
29     def put(self, request, *args, **kwargs):
30         return self.update(request, *args, **kwargs)
31 
32     def delete(self, request, *args, **kwargs):
33         return self.destroy(request, *args, **kwargs)
使用mixin

使用通用的基于类的视图

通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py模块。

 1 from rest_framework import mixins
 2 from rest_framework import generics
 3 
 4 class BookViewSet(generics.ListCreateAPIView):
 5 
 6     queryset = Book.objects.all()
 7     serializer_class = BookSerializers
 8 
 9 class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView):
10     queryset = Book.objects.all()
11     serializer_class = BookSerializers
12 
13 class PublishViewSet(generics.ListCreateAPIView):
14 
15     queryset = Publish.objects.all()
16     serializer_class = PublshSerializers
17 
18 class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView):
19     queryset = Publish.objects.all()
20     serializer_class = PublshSerializers
使用通用类

使用viewsets.ModelViewSet

urls.py:

1 url(r'^books/$', views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"),
2     url(r'^books/(?P<pk>d+)$', views.BookViewSet.as_view({
3                 'get': 'retrieve',
4                 'put': 'update',
5                 'patch': 'partial_update',
6                 'delete': 'destroy'
7             }),name="book_detail"),
urls

views.py:

1 class BookViewSet(viewsets.ModelViewSet):
2     queryset = Book.objects.all()
3     serializer_class = BookSerializers
views

认证与权限组件

认证组件

局部视图认证

在app01.service.auth.py:

通过用户的token值做认证

1 class Authentication(BaseAuthentication):
2 
3     def authenticate(self,request):
4         token=request._request.GET.get("token")
5         token_obj=UserToken.objects.filter(token=token).first()
6         if not token_obj:
7             raise exceptions.AuthenticationFailed("验证失败!")
8         return (token_obj.user,token_obj)
View Code

在views.py:

 1 def get_random_str(user):
 2     import hashlib,time
 3     ctime=str(time.time())
 4 
 5     md5=hashlib.md5(bytes(user,encoding="utf8"))
 6     md5.update(bytes(ctime,encoding="utf8"))
 7 
 8     return md5.hexdigest()
 9 
10 
11 from app01.service.auth import *
12 
13 from django.http import JsonResponse
14 class LoginViewSet(APIView):
15     authentication_classes = [Authentication,]
16     def post(self,request,*args,**kwargs):
17         res={"code":1000,"msg":None}
18         try:
19             user=request._request.POST.get("user")
20             pwd=request._request.POST.get("pwd")
21             user_obj=UserInfo.objects.filter(user=user,pwd=pwd).first()
22             print(user,pwd,user_obj)
23             if not user_obj:
24                 res["code"]=1001
25                 res["msg"]="用户名或者密码错误"
26             else:
27                 token=get_random_str(user)
28                 UserToken.objects.update_or_create(user=user_obj,defaults={"token":token})
29                 res["token"]=token
30 
31         except Exception as e:
32             res["code"]=1002
33             res["msg"]=e
34 
35         return JsonResponse(res,json_dumps_params={"ensure_ascii":False})
views

全局视图认证组件

settings.py配置如下:

1 REST_FRAMEWORK={
2     "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",]
3 }
settings

权限组件

局部视图权限

在app01.service.permissions.py中:

1 from rest_framework.permissions import BasePermission
2 class SVIPPermission(BasePermission):
3     message="SVIP才能访问!"
4     def has_permission(self, request, view):
5         if request.user.user_type==3:
6             return True
7         return False
View Code

在views.py:

1 from app01.service.permissions import *
2 
3 class BookViewSet(generics.ListCreateAPIView):
4     permission_classes = [SVIPPermission,]
5     queryset = Book.objects.all()
6     serializer_class = BookSerializers
views

全局视图权限

settings.py配置如下:

1 REST_FRAMEWORK={
2     "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
3     "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",]
4 }
settings

 

throttle(访问频率)组件

局部视图throttle

在app01.service.throttles.py中:

 1 from rest_framework.throttling import BaseThrottle
 2 
 3 VISIT_RECORD={}
 4 class VisitThrottle(BaseThrottle):
 5 
 6     def __init__(self):
 7         self.history=None
 8 
 9     def allow_request(self,request,view):
10         remote_addr = request.META.get('REMOTE_ADDR')
11         print(remote_addr)
12         import time
13         ctime=time.time()
14 
15         if remote_addr not in VISIT_RECORD:
16             VISIT_RECORD[remote_addr]=[ctime,]
17             return True
18 
19         history=VISIT_RECORD.get(remote_addr)
20         self.history=history
21 
22         while history and history[-1]<ctime-60:
23             history.pop()
24 
25         if len(history)<3:
26             history.insert(0,ctime)
27             return True
28         else:
29             return False
30 
31     def wait(self):
32         import time
33         ctime=time.time()
34         return 60-(ctime-self.history[-1])
throttles

在views.py中:

1 from app01.service.throttles import *
2 
3 class BookViewSet(generics.ListCreateAPIView):
4     throttle_classes = [VisitThrottle,]
5     queryset = Book.objects.all()
6     serializer_class = BookSerializers
views

全局视图throttle

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
    "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",]
}

内置throttle类

在app01.service.throttles.py修改为:

1 class VisitThrottle(SimpleRateThrottle):
2 
3     scope="visit_rate"
4     def get_cache_key(self, request, view):
5 
6         return self.get_ident(request)
throttles

settings.py设置:

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
    "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
    "DEFAULT_THROTTLE_RATES":{
        "visit_rate":"5/m",
    }
}

解析器

request类

django的request类和rest-framework的request类的源码解析

局部视图

from rest_framework.parsers import JSONParser,FormParser
class PublishViewSet(generics.ListCreateAPIView):
    parser_classes = [FormParser,JSONParser]
    queryset = Publish.objects.all()
    serializer_class = PublshSerializers
    def post(self, request, *args, **kwargs):
        print("request.data",request.data)
        return self.create(request, *args, **kwargs)

全局视图

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
    "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
    "DEFAULT_THROTTLE_RATES":{
        "visit_rate":"5/m",
    },
    "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.FormParser',]
}

序列化

ser = ServerSerializer(instance=queryset, many=True)
#instance接受queryset对象或者单个model对象,当有多条数据时候,使用many=True,单个对象many=False

看序列化代码

from django.shortcuts import render,HttpResponse,redirect
from django.views import View
from app01 import models

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers


class BookSerializers(serializers.Serializer):
    #我们先序列化写两个字段的数据,别忘了这里面的字段和model表中的字段变量名要一样
    title = serializers.CharField(max_length=32)
    price = serializers.DecimalField(max_digits=5, decimal_places=2)

    #一对多的处理
    # publish = serializers.CharField(max_length=32)  #返回对象
    publish_email = serializers.CharField(max_length=32, source='publish.email')  # source指定返回的多对一的那个publish对象的email数据,并且我们现在找到书籍的email,所以前面的字段名称就可以不和你的publish对应好了,随便取名字
    publish_name = serializers.CharField(max_length=32, source='publish.name')  # source指定返回的多对一的那个publish对象的其他字段数据,可以接着写字段,也就是说关联的所有的字段的数据都可以写在这里进行序列化

    #对多对的处理
    # authors = serializers.CharField(max_length=32) #bookobj.authors拿到的类似于一个models.Authors.object,打印的时候这是个None
    # authors = serializers.CharField(max_length=32,source="authors.all") #这样写返回的是queryset类型的数据,这样给前端肯定是不行的,所以按照下面的方法写
    authors = serializers.SerializerMethodField() #序列化方法字段,专门给多对多字段用的,然后下面定义一个方法,方法名称写法是这样的get_字段名,名字必须是这样
    def get_authors(self,obj): #参数写一个obj,这个obj是一个一个的书籍对象,然后我们通过书籍对象来返回对应的数据
        # author_list_values = obj.authors.all().values() #返回这样类型的数据也行,那么具体你要返回什么结构的数据,需要和前端人员沟通清楚,然后这里对数据进行加工
        #假如加工成的数据是这种类型的[ {},{} ],就可以按照下面的逻辑来写,我简单写的,肯定有更好的逻辑来加工这些数据
        author_list_values = []
        author_dict = {}
        author_list = obj.authors.all()
        for i in author_list:
            author_dict['name'] = i.name
            author_list_values.append(author_dict)
        return author_list_values


class BookView(APIView):
    def get(self,request):
        book_obj_list = models.Book.objects.all()
        s_books = BookSerializers(book_obj_list,many=True)
        return Response(s_books.data)

    def post(self,request):
        pass

serializer在内部就做了这点事儿,伪代码昂

创建一个序列化类

简单使用

开发我们的Web API的第一件事是为我们的Web API提供一种将代码片段实例序列化和反序列化为诸如json之类的表示形式的方式。我们可以通过声明与Django forms非常相似的序列化器(serializers)来实现。

models部分:

 1 from django.db import models
 2 
 3 # Create your models here.
 4 
 5 
 6 class Book(models.Model):
 7     title=models.CharField(max_length=32)
 8     price=models.IntegerField()
 9     pub_date=models.DateField()
10     publish=models.ForeignKey("Publish")
11     authors=models.ManyToManyField("Author")
12     def __str__(self):
13         return self.title
14 
15 class Publish(models.Model):
16     name=models.CharField(max_length=32)
17     email=models.EmailField()
18     def __str__(self):
19         return self.name
20 
21 class Author(models.Model):
22     name=models.CharField(max_length=32)
23     age=models.IntegerField()
24     def __str__(self):
25         return self.name
models 

views部分:

 1 from rest_framework.views import APIView
 2 from rest_framework.response import Response
 3 from .models import *
 4 from django.shortcuts import HttpResponse
 5 from django.core import serializers
 6 
 7 
 8 from rest_framework import serializers
 9 
10 class BookSerializers(serializers.Serializer):
11     title=serializers.CharField(max_length=32)
12     price=serializers.IntegerField()
13     pub_date=serializers.DateField()
14     publish=serializers.CharField(source="publish.name")
15     #authors=serializers.CharField(source="authors.all")
16     authors=serializers.SerializerMethodField()
17     def get_authors(self,obj):
18         temp=[]
19         for author in obj.authors.all():
20             temp.append(author.name)
21         return temp
22 
23 
24 class BookViewSet(APIView):
25 
26     def get(self,request,*args,**kwargs):
27         book_list=Book.objects.all()
28         # 序列化方式1:
29         # from django.forms.models import model_to_dict
30         # import json
31         # data=[]
32         # for obj in book_list:
33         #     data.append(model_to_dict(obj))
34         # print(data)
35         # return HttpResponse("ok")
36 
37         # 序列化方式2:
38         # data=serializers.serialize("json",book_list)
39         # return HttpResponse(data)
40 
41         # 序列化方式3:
42         bs=BookSerializers(book_list,many=True)
43         return Response(bs.data)
views

ModelSerializer

class BookSerializers(serializers.ModelSerializer):
      class Meta:
          model=Book
          fields="__all__"
          depth=1

提交post请求

  def post(self,request,*args,**kwargs):
       
        bs=BookSerializers(data=request.data,many=False)
        if bs.is_valid():
            # print(bs.validated_data)
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

重写save中的create方法

class BookSerializers(serializers.ModelSerializer):

      class Meta:
          model=Book
          fields="__all__"
          # exclude = ['authors',]
          # depth=1

      def create(self, validated_data):
        
          authors = validated_data.pop('authors')
          obj = Book.objects.create(**validated_data)
          obj.authors.add(*authors)
          return obj

单条数据的get和put请求

class BookDetailViewSet(APIView):

    def get(self,request,pk):
        book_obj=Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj)
        return Response(bs.data)

    def put(self,request,pk):
        book_obj=Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,data=request.data)
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

超链接API:Hyperlinked

class BookSerializers(serializers.ModelSerializer):
      publish= serializers.HyperlinkedIdentityField(
                     view_name='publish_detail',
                     lookup_field="publish_id",
                     lookup_url_kwarg="pk")
      class Meta:
          model=Book
          fields="__all__"
          #depth=1

urls部分:

urlpatterns = [
    url(r'^books/$', views.BookViewSet.as_view(),name="book_list"),
    url(r'^books/(?P<pk>d+)$', views.BookDetailViewSet.as_view(),name="book_detail"),
    url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"),
    url(r'^publishers/(?P<pk>d+)$', views.PublishDetailViewSet.as_view(),name="publish_detail"),
]

分页

简单分页

 1 from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
 2 
 3 class PNPagination(PageNumberPagination):
 4         page_size = 1
 5         page_query_param = 'page'
 6         page_size_query_param = "size"
 7         max_page_size = 5
 8 
 9 class BookViewSet(viewsets.ModelViewSet):
10 
11     queryset = Book.objects.all()
12     serializer_class = BookSerializers
13     def list(self,request,*args,**kwargs):
14 
15         book_list=Book.objects.all()
16         pp=LimitOffsetPagination()
17         pager_books=pp.paginate_queryset(queryset=book_list,request=request,view=self)
18         print(pager_books)
19         bs=BookSerializers(pager_books,many=True)
20 
21         #return Response(bs.data)
22         return pp.get_paginated_response(bs.data)
views

偏移分页

from rest_framework.pagination import LimitOffsetPagination
原文地址:https://www.cnblogs.com/zhaohuhu/p/9134386.html