1. 从request先说起

在Django原生的request里,请求的数据可以从request.GET或者request.POST里面取到。

需要注意的是,如果是POST请求,request.POST里面只能取到Content-Type=application/x-www-form-urlencoded 的数据,我们常用的json数据就只能从报文request.body里取。  b'{"a": 1,"b": "2"}'

So。。。

RestFramework的APIView 封装的新request就解决了这个问题,post请求的任何数据都可以从request.data里面拿到 ,而get请求需要在request.GET里面取。

并且它还保留了原生request的接口:request._request

2. 序列化

简单使用

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

准备models部分:

class Book(models.Model):
title=models.CharField(max_length=32)
price=models.IntegerField()
pub_date=models.DateField()
CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
category = models.IntegerField(choices=CHOICES, verbose_name="图书的类别")
publish=models.ForeignKey("Publish")
authors=models.ManyToManyField("Author")
def __str__(self):
return self.title class Publish(models.Model):
name=models.CharField(max_length=32)
email=models.EmailField()
def __str__(self):
return self.name class Author(models.Model):
name=models.CharField(max_length=32)
age=models.IntegerField()
def __str__(self):
return self.name

序列化类:

from rest_framework import serializers
from .models import Book class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(required=False)
title = serializers.CharField(max_length=32, validators=[my_validate]) # 自定义验证方法,校验优先级高于下面的validate_title方法 CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
category = serializers.ChoiceField(choices=CHOICES, source="get_category_display",
read_only=True) # read_only表示get方法拿到的值
w_category = serializers.ChoiceField(choices=CHOICES, write_only=True) # write_only 表示只进行操作不进行展示
pub_time = serializers.DateField() publisher = PublisherSerializer(
read_only=True) # 如若只想展示关键信息不想全部展示,只需要指定 publish = serializers.CharField(source="publish.name")
publisher_id = serializers.IntegerField(write_only=True) # authors = serializers.SerializerMethodField() # 所以处理多对多关系使用SerializerMethodField 方法
# def get_authors(self, obj): # get_字段 可以返回任何类型你想返回的值
# temp = []
# for author in obj.authors.all():
# temp.append(author.name)
# return temp # 处理多对多的get请求时有两种方法:1、如上,想展示什么字段都可以。2、如下,嵌套定义的序列化类,这里想展示的数量不能自己定制
author = AuthorSerializer(many=True, read_only=True) # many=True 表示有多个值 author_list = serializers.ListField(write_only=True) def create(self, validated_data): # post请求重新create方法
book = Book.objects.create(title=validated_data["title"], category=validated_data["w_category"],
pub_time=validated_data["pub_time"], publisher_id=validated_data["publisher_id"])
book.author.add(*validated_data["author_list"])
return book def update(self, instance, validated_data): # 更新重写update
instance.title = validated_data.get("title", instance.title)
instance.category = validated_data.get("category", instance.category)
instance.pub_time = validated_data.get("pub_time", instance.pub_time)
instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
if validated_data.get("author_list"):
instance.author.set(validated_data["author_list"])
instance.save()
return instance def validate_title(self, value): # 指定某个字段的验证
if "python" not in value.lower():
raise serializers.ValidationError("标题必须含有python")
return value def validate(self, attrs): # 联合验证
if attrs["w_category"] == 1 and attrs["publisher_id"] == 1:
return attrs
else:
raise serializers.ValidationError("分类以及标题不符合要求") def my_validate(value): # 额外为字段定制的校验
if "敏感信息" in value.lower():
raise serializers.ValidationError("不能含有敏感信息")
else:
return value

views部分:

from rest_framework.views import APIView
from rest_framework.response import Response # 可以用Django原生的Response,但是rest_framework的response做了一些封装。可以格式化你的json数据,并且浏览器访问会返回一个api页面
from .models import *
from django.shortcuts import HttpResponse
# from django.core import serializers class BookViewSet(APIView): def get(self, request, *args, **kwargs):
book_list = Book.objects.all()
# 序列化方式1:
# from django.forms.models import model_to_dict
# import json
# data=[]
# for obj in book_list:
# data.append(model_to_dict(obj))
# print(data)
# return HttpResponse("ok") # 序列化方式2:
# data=serializers.serialize("json",book_list)
# return HttpResponse(data) # 序列化方式3:
bs = BookSerializers(book_list, many=True) # many=true 代表了返回的是一个queryset列表,返回一个对象就不用加
return Response(bs.data)

ModelSerializer

class BookSerializer(serializers.ModelSerializer):
category_display = serializers.SerializerMethodField(read_only=True) # 自定义要获取的字段内容,在下面用get_<field-name>钩子来自定义内容
publisher_info = serializers.SerializerMethodField(read_only=True) # 此方法
authors = serializers.SerializerMethodField(read_only=True) def get_category_display(self, obj):
return obj.get_category_display() # orm中Choice_field显示中文的方式 def get_authors(self, obj): # get_字段:可以返回你想要返回的值
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):
# obj 是我们序列化的每个Book对象
publisher_obj = obj.publisher
return {"id": publisher_obj.id, "title": publisher_obj.title} class Meta:
model = Book
# fields = ["id", "title", "pub_time"]
fields = "__all__" # 遇到一对多或多对多默认取主键,所以需要自己定义取得数据
# depth = 1 #外键嵌套的层级数量 extra_kwargs = {"category": {"write_only": True}, "publisher": {"write_only": True},
"author": {"write_only": True}} # 使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数

提交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方法

post请求需要重写create()方法,put请求需要重写update()方法

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"),
]

3. 视图优化

上一节的视图部分:

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from django.shortcuts import HttpResponse
from django.core import serializers from rest_framework import serializers class BookSerializers(serializers.ModelSerializer):
class Meta:
model=Book
fields="__all__"
#depth=1 class PublshSerializers(serializers.ModelSerializer): class Meta:
model=Publish
fields="__all__"
depth=1 class BookViewSet(APIView): def get(self,request,*args,**kwargs):
book_list=Book.objects.all()
bs=BookSerializers(book_list,many=True,context={'request': request})
return Response(bs.data) def post(self,request,*args,**kwargs):
print(request.data)
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) class BookDetailViewSet(APIView): def get(self,request,pk):
book_obj=Book.objects.filter(pk=pk).first()
bs=BookSerializers(book_obj,context={'request': request})
return Response(bs.data) def put(self,request,pk):
book_obj=Book.objects.filter(pk=pk).first()
bs=BookSerializers(book_obj,data=request.data,context={'request': request})
if bs.is_valid():
bs.save()
return Response(bs.data)
else:
return HttpResponse(bs.errors) class PublishViewSet(APIView): def get(self,request,*args,**kwargs):
publish_list=Publish.objects.all()
bs=PublshSerializers(publish_list,many=True,context={'request': request})
return Response(bs.data) def post(self,request,*args,**kwargs): bs=PublshSerializers(data=request.data,many=False)
if bs.is_valid():
# print(bs.validated_data)
bs.save()
return Response(bs.data)
else:
return HttpResponse(bs.errors) class PublishDetailViewSet(APIView): def get(self,request,pk): publish_obj=Publish.objects.filter(pk=pk).first()
bs=PublshSerializers(publish_obj,context={'request': request})
return Response(bs.data) def put(self,request,pk):
publish_obj=Publish.objects.filter(pk=pk).first()
bs=PublshSerializers(publish_obj,data=request.data,context={'request': request})
if bs.is_valid():
bs.save()
return Response(bs.data)
else:
return HttpResponse(bs.errors)

上一节实现的逻辑代码

3.1 使用混合(mixins)

from web.models import *
from web.serializer import BookSerializer
from rest_framework.mixins import \
(ListModelMixin, CreateModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin)
from rest_framework.generics import GenericAPIView class BookViewSet(ListModelMixin,
CreateModelMixin,
GenericAPIView):
queryset = Book.objects.all() # 必须要指定一个quetyset
serializer_class = BookSerializer # 必须要指定一个序列化类 def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs) class BookDetailViewSet(RetrieveModelMixin, # 单条数据操作视图
UpdateModelMixin,
DestroyModelMixin,
GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)

3.2 使用通用的基于类的视图

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

from rest_framework import mixins
from rest_framework import generics class BookViewSet(generics.ListCreateAPIView): queryset = Book.objects.all()
serializer_class = BookSerializers class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializers

3.3 viewsets.ModelViewSet

urls.py:

urlpatterns = [
re_path(r'^books/$', views.BookViewSet.as_view({"get": "list", "post": "create"}), name="book_list"),
re_path(r'^books/(?P<pk>\d+)$', views.BookViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
}), name="book_detail"),
]

views.py:

from rest_framework.viewsets import ModelViewSet

class BookViewSet(ModelViewSet):
queryset = Book.objects.all() # 指定一个RetrieveModelMixin
serializer_class = BookSerializer # 指定一个序列化类

4. 认证与权限组件

4.1 认证组件

局部视图认证

先定义一个认证类:

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

class Authentication(BaseAuthentication): def authenticate(self,request):
token=request._request.GET.get("token")
token_obj=UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed("验证失败!")
return (token_obj.user,token_obj)

在views.py:

def get_random_str(user):
import hashlib,time
ctime=str(time.time()) md5=hashlib.md5(bytes(user,encoding="utf8"))
md5.update(bytes(ctime,encoding="utf8")) return md5.hexdigest() from app01.service.auth import * from django.http import JsonResponse
class LoginViewSet(APIView):
authentication_classes = [Authentication,]
def post(self,request,*args,**kwargs):
res={"code":1000,"msg":None}
try:
user=request._request.POST.get("user")
pwd=request._request.POST.get("pwd")
user_obj=UserInfo.objects.filter(user=user,pwd=pwd).first()
print(user,pwd,user_obj)
if not user_obj:
res["code"]=1001
res["msg"]="用户名或者密码错误"
else:
token=get_random_str(user)
UserToken.objects.update_or_create(user=user_obj,defaults={"token":token})
res["token"]=token except Exception as e:
res["code"]=1002
res["msg"]=e return JsonResponse(res,json_dumps_params={"ensure_ascii":False})

全局视图认证组件

settings.py配置如下:

REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",]
}

4.2 权限组件

局部视图权限

在app01.service.permissions.py中:

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

在views.py:

from app01.service.permissions import *

class BookViewSet(generics.ListCreateAPIView):
permission_classes = [SVIPPermission,]
queryset = Book.objects.all()
serializer_class = BookSerializers

全局视图权限

settings.py配置如下:

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

5. throttle(访问频率)组件

局部视图throttle

在app01.service.throttles.py中:

from rest_framework.throttling import BaseThrottle

VISIT_RECORD={}
class VisitThrottle(BaseThrottle): def __init__(self):
self.history=None def allow_request(self,request,view):
remote_addr = request.META.get('REMOTE_ADDR')
print(remote_addr)
import time
ctime=time.time() if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr]=[ctime,]
return True history=VISIT_RECORD.get(remote_addr)
self.history=history while history and history[-1]<ctime-60:
history.pop() if len(history)<3:
history.insert(0,ctime)
return True
else:
return False def wait(self):
import time
ctime=time.time()
return 60-(ctime-self.history[-1])

在views.py中:

from app01.service.throttles import *

class BookViewSet(generics.ListCreateAPIView):
throttle_classes = [VisitThrottle,]
queryset = Book.objects.all()
serializer_class = BookSerializers

全局视图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修改为:

class VisitThrottle(SimpleRateThrottle):

    scope="visit_rate"
def get_cache_key(self, request, view): return self.get_ident(request)

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", # 5:5次 m:分
}
}

6. 解析器

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',]
}

分页

简单分页

from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination

class PNPagination(PageNumberPagination):
page_size = 1
page_query_param = 'page'
page_size_query_param = "size"
max_page_size = 5 class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all()
serializer_class = BookSerializers
def list(self,request,*args,**kwargs): book_list=Book.objects.all()
pp=LimitOffsetPagination()
pager_books=pp.paginate_queryset(queryset=book_list,request=request,view=self)
print(pager_books)
bs=BookSerializers(pager_books,many=True) #return Response(bs.data)
return pp.get_paginated_response(bs.data)

偏移分页

from rest_framework.pagination import LimitOffsetPagination

Django 之RestFramework的更多相关文章

  1. Django 之 restframework 频率组件的使用

    Django 之 restframework 频率组件的使用以及源码分析 频率组件的使用 第一步,先写一个频率类,继承SimpleRateThrottle 一定要在这个类里面配置一个scop='字符串 ...

  2. 【Django】 rest-framework和RestfulAPI的设计

    [rest-framework] 这是一个基于django才能发挥作用的组件,专门用于构造API的. 说到API,之前在其他项目中我也做过一些小API,不过那些都是玩票性质,结构十分简单而且要求的设计 ...

  3. Django之restframework

    启动流程:引入rest_framework APP 在restframework中,GET数据可以通过request.query_params.get(xxx)获取,post数据可以通过request ...

  4. 通过django的rest-framework……(CBV)

    为什么不使用FBV,因为CBV重用性很高 先看一个例子: from django.views.generic.base import View from django.http import Http ...

  5. django使用restframework实现安全的api

    参考地址:https://github.com/tomchristie/django-rest-framework/ 一般如果在批量修改多的时候,不建议使用,一般在get请求,或者修改单条数据的时候使 ...

  6. django基于restframework的CBV封装

    一.models数据库映射 from django.db import models # Create your models here. class Book(models.Model): titl ...

  7. django使用RestFramework的Token认证

    今天实现的想法有点不正规: Django Rest framework的框架的认证,API都运行良好. 现在是要自己写一个function来实现用户的功能. 而不是用Rest 框架里的APIVIEW这 ...

  8. Django 之 restframework 版本控制的使用以及源码分析

    Django rest_framework 之 版本控制 一.何为版本控制: ​ 用于版本的控制 二.内置的版本控制类: from rest_framework.versioning import Q ...

  9. django使用restframework序列化查询集合(querryset)

    第一: pip install djangorestframework 第二: 在setting.py文件中的app添加名为: 'rest_framework', 第三:再项目的APP下面新建名为(可 ...

  10. django的RestFramework模块的源码分析

    一.APIView源码分析 查看源码的前提要知道,找函数方法必须先在自己的类中找,没有再往父类找,一层一层网上找,不能直接按ctrl点击 在我们自己定义的类中没有as_view方法的函数,所以肯定是继 ...

随机推荐

  1. [数据结构]克鲁斯卡尔(Kruskal)算法

    算法的概念 与Prim算法从顶点开始扩展最小生成树不同,Kruskal算法是一种按权值的递增次序选择合适的边来构造最小生成树的方法.假设N=(V,E)是连通网,对应的最小生成树T=(Vt,Et),Kr ...

  2. GreenPlum批量授权(PG未测试)

    创建一个自定义function create or replace function grant_all_exec(schema_name varchar,select_or_insert varch ...

  3. 使用gm/ID方法设计二级运算放大器

    1 设计指标 运算放大器采用图1所示的电路结构,电路中的电流源均采用共源共栅结构,可以获得较高的共模抑制比和电流复制精度.其性能指标为增益带宽积GBW=100MHz,负载电容CL=2pF.本设计采用的 ...

  4. pytorch 配置详细过程

    torch github 项目多 方便,api好调用 cpu版本 装torch 安装最新版本的就可以. torchvision 要版本对应 算法: torchvision版本号= torch版本号第一 ...

  5. 深度复盘-重启 etcd 引发的异常

    作者信息: 唐聪.王超凡,腾讯云原生产品中心技术专家,负责腾讯云大规模 TKE 集群和 etcd 控制面稳定性.性能和成本优化工作. 王子勇,腾讯云专家级工程师, 腾讯云计算产品技术服务专家团队负责人 ...

  6. layui富文本的使用注意事项以及拓展

    一.引入layui.js文件 二.初始化编辑器 PS:layedit.set({}) 方法必须要在初始化编辑器之前 var editIndex, layedit, layer; $(function ...

  7. Mysql中where if问题

    网上关于Mybatis中where与if的说法乱七八糟的,Myabtis官网写的很清晰.为了防止误导他人,在此记录: 1.where语句+< if > 标签 <select id=& ...

  8. 【TS】数组和元组

    数组 在ts中,定义数组类型语法: let 变量名 : 数据类型[] = [值1,值2,值3] let arr1 : number[] = [1,2,3,4] console.log(arr1); / ...

  9. 安卓逆向 IDA 静态调试分析

    1.找到我们分析的接口 2.F5进入C伪代码 修正一下参数,IDA无法正常识别 jstring __fastcall Java_com_example_sfs_MainActivity_getText ...

  10. 郁金香-了解MFC信息机制

    控件的事件 窗口的信息