django rest framework框架

1. CBV

CBV基于反射实现根据请求方式不同,执行不同的方法

原理: url -> view方法 -> dispatch方法(反射执行其他:GET/POST/DELETE/PUT)

 

1)简单案例:

views.py:

from django.views import View

class MyBaseView(object):
    def dispatch(self, request, *args, **kwargs):
        ret = super(StudentView, self).dispatch(request, *args, **kwargs)
        return ret


class StudentsView(MyBaseView, View):
    
    # 只展示4个常用的
    def get(self, request, *args, **kwargs):
        return HttpResponse('GET')
   
    def post(self, request, *args, **kwargs):
        return HttpResponse('POST')

    def put(self, request, *args, **kwargs):
        return HttpResponse('PUT')

    def delete(self, request, *args, **kwargs):
        return HttpResponse('DELETE')

urls.py:

urlpatterns = [
    url(r'^users/', views.UsersView.as_view()),
]

    

 

2)CSV解决csrf认证问题

方式一:

from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
    

class StudentsView(MyBaseView, View):
    # 加在单独的方法中无效,只能加载dispatch方法中
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        ret = super(StudentView, self).dispatch(request, *args, **kwargs)
        return ret
    
    # 只展示4个常用的
    def get(self, request, *args, **kwargs):
        return HttpResponse('GET')
   
    
    def post(self, request, *args, **kwargs):
        return HttpResponse('POST')

    def put(self, request, *args, **kwargs):
        return HttpResponse('PUT')

    def delete(self, request, *args, **kwargs):
        return HttpResponse('DELETE')

方式二

from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
    

@method_decorator(csrf_exempt, name='dispatch')
class StudentsView(MyBaseView, View):
    
    # 只展示4个常用的
    def get(self, request, *args, **kwargs):
        return HttpResponse('GET')
   
    
    def post(self, request, *args, **kwargs):
        return HttpResponse('POST')

    def put(self, request, *args, **kwargs):
        return HttpResponse('PUT')

    def delete(self, request, *args, **kwargs):
        return HttpResponse('DELETE')

 

 

 

2. restful规范

如果一个架构符合REST的约束条件和原则,即称其为RESTful框架

1) 面向资源编程:每个URL代表一种资源,URL中尽量不要用动词,用名词

2)根据method不同,进行不同的操作:

  • GET  从服务器取出资源(一项或多项)
  • POST  在服务器新建一个资源
  • PUT  在服务器更新资源(客户端提供改编后的完整资源)
  • PATCH  在服务器更新资源(客户端提供改变的属性)
  • DELETE  从服务器删除资源

3)在URL中体现版本:

https://www.example.com/v1/test

https://v1.example.com/test

4)在URL中体现是否是API

https://api.example.com  尽量将API部署在专用域名(存在跨域问题)

https://example.org/api/

5)在URL中的过滤条件

http://api.example.com/v1/zoos?limit=10  指定返回记录的数量

http://api.example.com/v1/zoos?page=2?per_page=100  指定第几页,以及每页的记录数

6)尽量使用HTTPS

7)响应时设置状态码

  • 1** 信息,服务器受到请求,需要请求者继续执行操作
  • 2**  成功,操作被成功接受并处理
  • 3**  重定向,需要进一步的操作以完成请求
  • 4**  客户端错误,请求包含语法错误或无法完成请求
  • 5**  服务器错误,服务器在处理请求的过程中发生了错误

8)返回值

  • GET  /collection:返回资源对象的列表(数组)
  • GET  /collection/resource:返回单个资源对象
  • POST /collection:返回新生成的资源对象
  • PUT  /collection/resource:返回完整的资源对象
  • PATCH  /collection/resource:返回完整的资源对象
  • DELETE  /collection/resource:返回一个空文档

9)返回错误信息,状态码是4XX时,应返回错误信息,error当作key

{error: "Invalid api key"}

10)Hypermedia API,RESTFUL API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档也知道下一步应该做什么

{"link": {

  "rel": "collection https://www.example.com/zoos",

  "href": "https://api.example.com/zoos",

  "title": "list of zoos",

  "type": "application/vnd.yourformat+json"

}}

 

 

 

2. restframework实现序列化与反序列化

ModelSerializer

urls.py

from django.urls import path
from .views import BookView, BookEditView, BookModelViewSet

urlpatterns = [
    # path('list', BookView.as_view()),
    # path('retrieve/', BookEditView.as_view()),
]
    path('list', BookModelViewSet.as_view({"get": "list", "post":"create"})),
    path('retrieve/', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]

serializers.py 序列化组件

from rest_framework import serializers
from .models import Book

class PublisherSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)

class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)

def my_validate(value):
    if "敏感信息" in value.lower():
        raise serializers.ValidationError("不能含有敏感信息")
    else:
        return value

class BookSerializer(serializers.ModelSerializer):
    category_display = serializers.SerializerMethodField(read_only=True)
    publisher_info = serializers.SerializerMethodField(read_only=True)
    authors = serializers.SerializerMethodField(read_only=True)

    def get_category_display(self, obj):
        return obj.get_category_display()

    def get_authors(self, obj):
        authors_query_set = obj.author.all()
        return [{"id":author_obj.id, "name":author_obj.name} for author_obj in authors_query_set]
    
    def get_publisher_info(self, obj):
        publisher_obj = obj.publisher
        return [{"id": publisher_obj.id, "title":publisher_obj.title}]

    class Meta:
        model = Book
        fields = "__all__"
        extra_kwargs = {"category": {"write_only": True}, "publisher": {"write_only": True}, "author": {"write_only": True}}

views.py 视图组件

from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet
from rest_framework.response import Response
from .serializers import BookSerializer
from models import Book
from rest_framework.viewsets import ViewSetMixin

# 以下为框架提供的:
# from rest_framework import views
# from rest_framework import generics
# from rest_framework import mixins
# from rest_framework import viewsets


class GenericAPIView(APIView):
    query_set = None
    serializer_class = None

    def get_queryset(self):
        return self.query_set

    def get_serializer(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)

class ListModelMixin(object):
    def list(self, request):
        queryset = self.get_queryset()
        ret = self.get_serializer(queryset, many=True)
        return Response(ret.data)

class CreateModelMixin(object):
    def create(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors)

class RetrieveModelMixin(object):
    def retrieve(self, request, id):
        queryset = self.get_queryset().filter(id=id).first()
        ret = self.get_serializer(queryset)
        return Response(ret.data) 

class UpdateModelMixin(object):
    def update(self, request, id):
        queryset = self.get_queryset().filter(id=id).first()
        serializer = self.get_serializer(queryset, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors)

class DestroyModelMixin(object):
    def destroy(self, request, id):
        queryset = self.get_queryset().filter(id=id).first()
        queryset.delete()
        return Response("")

class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
    pass


class BookView(ListCreateAPIView):
    query_set = Book.objects.all()
    serializer_class = BookSerializer
    
    def get(self, request):
        return self.list(request)
    
    def post(self, request):
        return self.create(request)

class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    pass

class BookEditView(RetrieveUpdateDestroyAPIView):
    query_set = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, id):
        self.retrieve(request, id)
    
    def put(self, request, id):
        self.update(request, id)

    def delete(self, request, id):
        self.destroy(request, id)

class ModelViewSet(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    pass

class BookModelViewSet(ModelViewSet):
    query_set = Book.objects.all()
    serializer_class = BookSerializer


        

 

 

 

3.DRF路由组件

from django.urls import path
from .views import BookView, BookEditView, BookModelViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r"", BookModelViewSet)

urlpatterns = [
    
]

urlpatterns += router.urls

# 如对一个表没有增删改功能,不要使用这种方法暴露过多的url

 

 

 

4. DRF的版本控制组件

实现流程的简单示例:

在Django的settings.py文件中设置与REST_FRAMEWORK相关的配置:

REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",    
    # "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
    "DEFAULT_VERSION": "v1",
    "ALLOWED_VERSIONS": "v1, v2",
    "VERSION_PARAM": "version"
}

在Django下创建utils文件夹,并在其下创建version.py文件:

from rest_framework import versioning

class MyVersion(object):
    def determine_version(self, request, *args, **kwargs):
        # 例:版本号携带在过滤条件中 xxxx?version=v1
        version = request.query_params.get("version", "v1")
        return version

views.py文件:

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

class DemoView(APIView):
    def get(self, request):
        # 得到版本号,根据版本号的不同返回不同的信息
        if request.version == "v1":
            return Response("v1版本的数据")
        elif request.version == "v2":
            return Response("v2版本的数据")
        return Response(“不存在的版本”)

 

 

 

5. DRF认证组件

models.py文件:

from django.db import models

class User(models.Model):
    username = models.CharField(max_length)
    pwd = models.CharField(max_length)
    token = models.UUIDField()
    type = models.IntegerField(choices=((1, "vip"), (2, "vvip"), (3, "普通")), default=3)

urls.py文件:

from django.urls import path
from .views import LoginView

urlpatterns = [
    path(r"login", LoginView.as_view()),
]

  views.py文件

from rest_framework.views import APIView
from rest_framework.response import Response
import uuid
from models import User
from utils.auth import MyAuth

class LoginView(APIView):
    def post(self, request):
        username = request.data.get("username")
        pwd = request.data.get("pwd")
        token = uuid.uuid4()
        
        User.objects.create(username=username, pwd=pwd, token=token)
        return Response("创建用户成功")
        

class TestView(APIView):
    authentication_classes = [MyAuth,]
    def get(self, request):
        print(request.user)
        print(request.auth)
        return Response("认证测试")


在utils文件下创建auth.py文件:

from rest_framework.exceptions import AuthenticationFailed
from xxx.models import User
from rest_framework.authentication import BaseAuthentication

class MyAuth(BaseAuthentication):
    def authenticate(self, request):
        # 真正实现认证的地方
        # 做认证,看是否登录
        # 从url过滤条件里拿到token
        # 去数据库看token是否合法
        # 合法的token能够获取用户信息
        token = request.query_params.get("token", "")
        if not token:
            raise AuthenticationFailed("未携带token")
        user_obj = User.objects.filter(token=token).first()
        if not user_obj:
            raise AuthenticationFailed("token不合法")
        return (user_obj, token)

 

 

 

6. DRF权限组件

models.py文件:

from django.db import models

class User(models.Model):
    username = models.CharField(max_length)
    pwd = models.CharField(max_length)
    token = models.UUIDField()
    type = models.IntegerField(choices=((1, "vip"), (2, "vvip"), (3, "普通")), default=3)

在utils文件夹下创建permission.py文件:

from rest_framework.permissions import BasePermission


class MyPermission(BasePermission):
    message = "您没有权限“
    
    def has_permission(self, request, view):
        # 判断用户是否有权限
        user_obj = request.user
        if user_obj.type == 3:
            return False
        return True

        

views.py文件:

from rest_framework.views import APIView
from rest_framework.response import Response
import uuid
from models import User
from utils.auth import MyAuth
from utils.permission import MyPermisson

class LoginView(APIView):
    def post(self, request):
        username = request.data.get("username")
        pwd = request.data.get("pwd")
        token = uuid.uuid4()
        
        User.objects.create(username=username, pwd=pwd, token=token)
        return Response("创建用户成功")
        

class TestView(APIView):
    authentication_classes = [MyAuth,]
    permission_classes = [MyPermisson,] # 权限配置
    def get(self, request):
        print(request.user)
        print(request.auth)
        return Response("认证测试")


 

 

 

7. DRF频率组件

1)自己实现限流类

 utils文件夹下创建throttle.py文件:

from rest_framework.throttling import BaseThrottle

VISIT_RECORD = {}

# 简单实现频率控制
class MyThrottle(BaseThrottle):
    def __init__(self):
        self.history = None

    def allow_request(self, request, view):
        # 实现限流的逻辑
        # 以IP限流
        # 访问列表{IP: [time1, time2, time3]}
        # 1. 获取请求的IP地址
        ip = request.META.get("REMOTE_ADDR")
        # 2. 判断IP地址是否在访问列表:不在,需要给访问列表添加key,value;在,需要把这个IP的访问记录,把当前时间加入到列表
        now = time.time()
        if ip not in VISIT_RECORD:
            VISIT_RECORD[ip] = [now, ]
            return True
        history = VISIT_RECORD[ip]
        history.insert(0, now)
        # 3. 确保列表里最新访问时间以及最老的访问时间差为1分钟
        while history and history[0] - history[-1] > 60:        
            history.pop()
        self.history = history
        # 4. 得到列表长度,判断是否是允许的次数
        if len(history) > 3:
            return False
        else:
            return True

    def wait(self):
        # 返回需要再等多久才能访问
        time = 60 - (self.history[0] - self.history[-1])
        return time
        
        

views.py文件

from rest_framework.views import APIView
from rest_framework.response import Response
import uuid
from models import User
from utils.auth import MyAuth
from utils.permission import MyPermisson
from utils.throttle import MyThrottle

class LoginView(APIView):
    def post(self, request):
        username = request.data.get("username")
        pwd = request.data.get("pwd")
        token = uuid.uuid4()
        
        User.objects.create(username=username, pwd=pwd, token=token)
        return Response("创建用户成功")
        

class TestView(APIView):
    authentication_classes = [MyAuth,]
    permission_classes = [MyPermisson,] # 权限配置
    throttle_classes = [MyThrottle,] # 限流配置

    def get(self, request):
        print(request.user)
        print(request.auth)
        return Response("认证测试")


 

2)框架实现的限流类

settings.py文件:

REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",    
    # "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
    "DEFAULT_VERSION": "v1",
    "ALLOWED_VERSIONS": "v1, v2",
    "VERSION_PARAM": "version",
    "DEFAULT_THROTTLE_RATES": {
        "WD": "3/m"
    }
}

throttle.py文件:

from rest_framework.throttling import BaseThrottle, SimpleRateThrottle

VISIT_RECORD = {}

# 简单实现频率控制
class MyThrottle(SimpleRateThrottle):
    scope = "WD"
    
    def get_cache_key(self, request, view):
        key = self.get_ident(request)
        return key

 

 

8. DRF分页器

1)pageNumber分页

utils文件夹下创建pagination.py文件:

from rest_framework.pagination import PageNumberPagination

class MyPagination(PageNumberPagination):
    page_size = 2
    page_query_param = "page"
    page_size_query_param = "size"
    max_page_size = 3

views.py文件:

from rest_framework.views import APIView
from xxx.models import Book
from xxx.serializers import BookSerializer
from utils.pagination import MyPagination

class BookView(APIView):
    def get(self, request):
        queryset = Book.objects.all()
        # 1. 实例化分页器对象
        page_obj = MyPagination()
        # 2. 调用分页方法去分页queryset
        page_queryset = page_obj.paginate_queryset(queryset, request, view=self)
        # 3. 把分页好的数据序列化返回
        # 4. 带着上一页下一页链接的响应
        ser_obj = BookSerializer(page_queryset, many=True)
        
        return page_obj.get_paginated_response(ser_obj.data)

 

2)limitoffset分页

from rest_framework.pagination import LimitOffsetPagination

class MyPagination(LimitOffsetPagination):
    default_limit = 1
    limit_query_param = "limit"
    offset_query_param = "offset"
    max_limit = 3

 

3)Cursor分页

from rest_framework.pagination import CursorPagination

class MyPagination(CursorPagination):
    cursor_query_param = "cursor"
    page_size = 2
    ordering = "-id"

 

 

 

9. 解析器

解析器的作用就是服务端接受客户端传过来的数据,把数据解析成自己想要的数据类型的过程

1)Django的解析器

Django会把解析好的数据放入request.body中

from django.views import View
from django.http import HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import parsers

class DjangoView(View):
    def get(self, request):
        print(type(request))
        return HttpResponse("dajngo解析器测试")
   

2)DRF解析器

from django.views import View
from django.http import HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import parsers

class DjangoView(View):
    def get(self, request):
        print(type(request))
        return HttpResponse("django解析器测试")

class DRFView(APIView)
    parser_classes = [parsers.JSONParser, ] # 一般不去自己配置
    def get(self, request):
        print(type(request))
        return HttpResponse("DRF解析器测试")

 

 

 

补充

1. django常用中间件

process_request

process_view

process_response

process_exception

process_render_template

 

2. django的csrf是如何实现的?

from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
@csrf_protect

process_view方法

--检查视图是否被csrf_exempt(免除csrf认证)

--去请求体或cookie中获取token

 

3. 跨域

CORS即Cross Origin Resource Sharing跨域资源共享

1)简单请求和复杂请求:

HTTP方法是下列方法之一:HEAD, GET, POST

HTTP头信息不超过以下几种字段:Accept, Accept-Language, Content-Language, Last-Event-ID

Content-Type只能是下列类型中的一个:application/x-www-form-urlencoded, multipart/form-data, text/plain

任何一个不满足上述要求的请求,即会被认为是复杂请求,复杂请求会先发出一个预请求,也叫预检,OPTIONS请求

 

2)浏览器的同源策略

跨域是因为浏览器的同源策略导致的,也就是说浏览器会阻止非同源的请求。域名不同、端口不同都属于非同源的。浏览器只阻止表单以及ajax请求,并不会阻止src请求,所以类似cdn, 图片等src请求都可以发

 

3)解决跨域

(1)JSONP,实现原理是根据浏览器不组织src请求入手来实现的

class Test(APIView):
    def get(self, request):
        callback = request.query_params.get("callback", "")
        ret = callback + "(" + "'success'" + ")"
        return HttpResponse(ret)

JSONP解决跨域只能发送get请求,并且实现起来需要前后端交互比较多

 

(2)添加响应头解决跨域(推荐方法)

在项目下创建middlewares.py文件,并添加在settings.py文件中添加中间件

from django.utils.deprecation import MiddlewreMixin

class MyCors(MiddlewareMixin):
    def process_response(self, request, response):
        response["Access-Control-Allow-Origin"] = "*"
        if request.method == "OPTIONS":
            response["Access-Control-Allow-Methods"] = "POST, PUT, DELETE"
            response["Access-Control-Allow-Headers"] = "content-type"
        return response

 

4. Django的content-type组件

简单示例,models.py文件:

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

class Food(models.Model):
    title = models.CharField(max_length=32)

class Fruit(models.Model):
    title = models.CharField(max_length=32)

class Coupon(models.Model):
    title = models.CharField(max_length=32)
    content_type = models.ForeignKey(to=ContentType)
    object_id = models.IntegerField()
    content_obj = GenericForeignKey("content_type", "object_id")


views.py文件:

from rest_framework.views import APIView
from rest_framework.response import Response
from models import Food, Coupon

class DemoView(APIView):
    def get(self, request):
        food_obj = Food.objects.filter(id=1).first()
        # 创建优惠券
        # Coupon.objects.create(title="牛奶八折", content_object=food_obj)
        
        # 查询牛奶有哪些优惠券
        coupons = food_obj.couponse.all()
        # 优惠券差对象
        coupon_obj = Coupon.objects.filter(id=1).first()
        content_obj = coupon_obj.content_object

        # 通过ContentType表找表模型
        content = ContentType.objects.filter(app_label="demo", model="food").first()
        model_class = content.model_class()
        ret = model_class.objects.all()
        print(ret)
                
        return Response("ContentType测试")

 

 

 

5. Redis以及python操作Redis

1)Redis是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库

2)Redis有以下特点:

    -- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用

    -- Redis支持五种数据类型

    -- Redis支持数据库备份

3)Redis的优势:

    -- Redis性能极高,读的速度是110000次/s,写的速度是81000次/s

    -- Redis丰富的数据类型,String, Lists, Hashes, Sets以及Ordered Sets

    -- Redis的所有操作都是原子性的,意思就是要么成功执行,要么完全失败不执行,多个操作支持事务。即MULTI和EXEC指令包起来

    -- Redis有丰富的特性,支持publish/subscribe,通知,key过期等等特性

4)Redis配置:

    -- 可以通过redis-cli进入交互模式,使用config命令查看或设置配置项。也可以进入配置文件用vim编辑器进行修改

# 获取所有配置项
config get *

# 获取单个配置项
config get loglevel

# 编辑配置
config set loglevel "notice"

5)Redis数据类型

-- String字符串

    -- redis的string可以包含任何数据,包括图片以及序列化的对象,一个键最大能存储512mb

-- Hash 哈希

    -- redis的hash是一个String类型的key和value的映射表,hash特别适合存储对象,类比python字典

-- List 列表

    -- redis的list是简单的字符串列表,按照插入顺序排列,可以从两端进行添加,类似于双向链表,列表还可以进行阻塞

-- Set 集合

    -- redis的set是字符串类型的无序且不重复集合。集合是通过哈希表实现的,所以添加、删除、查找的时间复杂度都是O(1)

-- Zset 有序集合

    -- redis的zset和set一样,不同的是每个元素都会关联一个double类型的分数,redis正是通过对分数的排序对集合进行有序存储。

6)Python操作redis

import redis

conn = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)
conn.set("n1", "v1")
conn.hset("n2", "k2", "v2")
conn.hmset("n3", {"k3": "v3", "k4": "v4"})

ret1 = conn.get("n1")
ret2 = conn.hget("n2", "k2")
ret3 = conn.hget("n3", "k3")
ret4 = conn.hget("n3", "k4")
ret5 = conn.hgetall("n3")

print(ret1)
print(ret2)
print(ret3)
print(ret4)
print(ret5)

7)发布者、订阅者模型

pub.py文件:

import redis

conn = redis.Redis(decode_responses=True)

conn.publish("Steven", "Back to the real world")

sub.py文件:

import redis

conn = redis.Redis(decode_responses=True)

# 生成一个订阅者对象
pubsub = conn.pubsub()

# 订阅一个消息
pubsub.subscribe("Steven")

# 创建一个接受
while True:
    print("Start to work")
    msg = pubsub.parse_response()
    print(msg)

 

你可能感兴趣的:(django rest framework框架)