0801 RESTAPI设计,DRF 序列化

1.内容回顾
    1.restframework serializer(序列化)的简单使用
        
        QuereySet([obj,obj,obj])  -->  JSON格式数据
        
    安装和导入:
        pip install djangorestframework
        
        from rest_framework import serializer
        
        1.创建一个类,类一定要继承serializer.Serializers
        
        2.chocie字段和FK字段都可以通过source来获取对应的值
        
        3.多对多字段可以通过使用serializer.SerializerMethod
    
                声明序列化器:
                def get_tag(self, obj):
                    tag_list = []
                    for i in obj.tag.all():
                        tag_list.append(i.name)
                    return tag_list
                    (比较麻烦)
        2.使用 ModelSerializer(简单)
        ORM绑定的序列化
            通过配置class Meta:
                        model = 表名
                        fields = ['字段', ...]
                        depth = 1
                        
2. 今日内容
    1. RESTful风格API介绍

一、什么是RESTful

REST -- REpresentational State Transfer,英语的直译就是“表现层状态转移”。如果看这个概念,估计没几个人能明白是什么意思。
那下面就让我来用一句人话解释一下什么是RESTful:URL定位资源,用HTTP动词(GET,POST,PUT,DELETE)描述操作。

 

Resource:资源,即数据。

Representational:某种表现形式,比如用JSON,XML,JPEG等;

State Transfer:状态变化。通过HTTP动词实现。

REST与技术无关,代表的是一种软件架构风格,RESTRepresentational State Transfer的简称,中文翻译为表征状态转移

REST从资源得角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态。

所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性。

 

二、RESTful API设计

API与用户的通信协议

总是使用HTTPs协议域名

https://api.example.com        尽量将API部署在专用域名

https://example.org/api/       API很简单

 
版本

1.  将版本信息放在URL中,如:https://api.example.com/v1/

2. 将版本信息放在请求头中。

 
路径

视网络上任何东西都是资源,均使用名词表示(可复数)

https://api.example.com/v1/zoos

https://api.example.com/v1/animals

https://api.example.com/v1/employees

 
method

GET      :从服务器取出资源(一项或多项)

POST    :在服务器新建一个资源

PUT      :在服务器更新资源(客户端提供改变后的完整资源)

PATCH  :在服务器更新资源(客户端提供改变的属性)

DELETE :从服务器删除资源

 
过滤

通过在url上传参的形式传递搜索条件

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

https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置

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

https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序

https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件

 
状态码

200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 错误处理 状态码是4xx时,应返回错误信息,error当做key。     { error: "Invalid API key" }
返回结果 针对不同操作,服务器向用户返回的结果应该符合以下规范 GET
/collection:返回资源对象的列表(数组) GET /collection/resource:返回单个资源对象 POST /collection:返回新生成的资源对象 PUT /collection/resource:返回完整的资源对象 PATCH /collection/resource:返回完整的资源对象 DELETE /collection/resource:返回一个空文档 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" }} 更多内容,请参考:阮一峰的Blog

二、基于Django实现RESTful API

 1.RESTful API之url设计

 路由分发:

 在原先的URL中添加:url(r'api/', include(app01_urls)),  include表示导入py文件

app01下新建路径文件:app01_urls.py

 

from django.conf.urls import urlfrom app01 import views

 

urlpatterns = [

    url(r'article/', views.Article.as_view()),

    url(r'article/(?P<pk>d+)', views.ArticleDetail.as_view(), name='article-detail'),

    url(r'school/(?P<id>d+)', views.SchoolDetail.as_view(), name='school-detail111'),

 

    # 评论

    # url(r'comment_list/', ),

    # url(r'add_comment/', ),

    # url(r'delete_comment/', ),

    # url(r'edit_comment/', ),

    url(r'comment/', views.Comment.as_view()),

]

 

2.RESTful API之urls视图

针对4种请求方式,使用FBV

def comment(request):

    if request.method == "GET":

        return HttpResponse("获取评论")

    elif request.method == "POST":

        return HttpResponse("创建新评论")

    elif request.method == "PUT":

        return HttpResponse("修改评论")

    elif request.method == "DELETE":

        return HttpResponse("删除评论")

这样写的话,一个4if判断,就会有很冗长的代码。不利与维护,推荐使用CBV方式

CBV方式

from django import viewsclass Comment(views.View):

    def get(self, request):

        return HttpResponse("获取评论")

 

    def post(self, request):

        return HttpResponse("创建新评论")

 

    def put(self, request):

        return HttpResponse("修改评论")

 

    def delete(self, request):

        return HttpResponse("删除评论")

三、 APIView

 from rest_framework import APIView  
 class Comment(views.View):

           pass     

    --------  之前的写法 ↑  ----------

    class APIView(views.View):

        扩展的功能

        self.request = Resquest(

            self._request = request(Django的request)

        )

        self.request.data  封装好的数据属性,POSTPUT请求携带的数据都可以从这里取

        pass

 

  class Comment(APIView):

        pass

1. APIView与View的区别

  1. APIViewView的子类
  2. 传递给请求处理程序的request实例是REST框架的请求实例,而不是DjangoHttpRequest实例
  3. 处理程序返回的基于REST框架的Response,而不是DjangoHttpResponse,视图函数将会管理内容协商,然后设置正确的渲染方式
  4. 任何APIException将会被捕捉,然后转换成合适的response对象
  5. 接收到的请求首先被认证,然后赋予相应的权限,然后通过节流器分发给相应的请求处理函数,类似.get().post()

 

APIView是专门写API的视图函数,结合serializers,非常方便做序列化!

关于APIView的源码解析,请参考文章:

https://blog.csdn.net/u013210620/article/details/79857654

 

取数据

get:

  request._request ( 原request )

  request.GET == request._request.GET 

post:

  request.data ( 得到 json 数据,也可得到 urlencode 数据)

    都是 rest_framework  APIView 的功劳,

  如果使用 View , request.post( 只能得到 urlencode 的数据,不能得到 json 数据,只能从 request.body 中取) 

 3. serializer
        1. ModelSerializer
            1. 基于APIView实现的GETPOST

    2. POST过来的数据进行校验
            3. 保存数据

在app01下面创建一个文件,专门放序列化的

from app01.models import Comment, Article, School

from rest_framework import serializers

from rest_framework.validators import ValidationError

 # 序列化评论的类
  class CommentSerializer(serializers.ModelSerializer):
# content = serializers.CharField(error_messages={"required": '内容不能为空'}) # article_detail = serializers.SerializerMethodField(read_only=True) # 用于做校验的钩子函数,类似于form组件的clean_字段名 def validate_content(self, value): if '' in value: raise ValidationError('不符合社会主义核心价值观!') else: return value # 全局的钩子 # def validate(self, attrs): # # self.validated_data # 经过校验的数据 类似于form组件中的cleaned_data # # 全局钩子 # pass class Meta: model = Comment fields = "__all__" # depth = 1 # 定义额外的参数 extra_kwargs = { "content": { "error_messages": { "required": '内容不能为空', } }, "article": { "error_messages": { "required": '文章不能为空' } } } # 文章的序列化类
  class ArticleModelSerializer(serializers.ModelSerializer):
# type_str = serializers.CharField(source='get_type_display', read_only=True) class Meta: model = Article # 绑定的ORM类是哪一个 fields = "__all__" # ["id", "title", "type"] # depth = 1 # 官方推荐不超过10层 # 文章超链接序列化 class ArticleHyperLinkedSerializer(serializers.HyperlinkedModelSerializer): school = serializers.HyperlinkedIdentityField(view_name='school-detail111', lookup_url_kwarg='id') class Meta: model = Article # 绑定的ORM类是哪一个 fields = ["id", "title", "type", "school"] # 学校的序列化 class SchoolSerializer(serializers.ModelSerializer): class Meta: model = School fields = "__all__" 参数解释: source 表示来源 lookup_field 表示查找字段 lookup_url_kwarg 表示路由查找的参数,pk表示主键 view_name 它是指urls定义的name值,一定要一一对应。

models.py

from django.db import models

# 文章表
class Article(models.Model):
    title = models.CharField(max_length=32, unique=True, error_messages={"unique": "文章标题不能重复"})
    # 文章发布时间
    # auto_now每次更新的时候会把当前时间保存
    create_time = models.DateField(auto_now_add=True)
    # auto_now_add 第一次创建的时候把当前时间保存
    update_time = models.DateField(auto_now=True)
    # 文章的类型
    type = models.SmallIntegerField(
        choices=((1, "原创"), (2, "转载")),
        default=1
    )
    # 来源
    school = models.ForeignKey(to='School', on_delete=models.CASCADE)
    # 标签
    tag = models.ManyToManyField(to='Tag')

# 文章来源表
class School(models.Model):
    name = models.CharField(max_length=16)

# 文章标签表
class Tag(models.Model):
    name = models.CharField(max_length=16)

# 评论表
class Comment(models.Model):
    content = models.CharField(max_length=128)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)


GET:
class Comment(APIView):

 

    def get(self, request):

        res = {"code": 0}

        # print(self.request)  # rest framework封装好的request

        # print(self.request.data)

        # print(self.request._request.GET.get("age"))  # Django原来的request对象

        # 数据

        all_comment = models.Comment.objects.all()

        # 序列化

        ser_obj = app01_serializers.CommentSerializer(all_comment, many=True)

        res["data"] = ser_obj.data

        return Response(res)

POST:
def post(self, request):
    res = {"code": 0}
    # 去提交的数据
    comment_data = self.request.data
    # 对用户提交的数据做校验
    ser_obj = app01_serializers.CommentSerializer(data=comment_data)
    if ser_obj.is_valid():
        # 表示数据没问题,可以创建
        ser_obj.save()  #创建数据,写入数据库
    else:
        # 表示数据有问题
        res["code"] = 1
        res["error"] = ser_obj.errors
    return Response(res)

    return HttpResponse("创建新评论")

#文章CBV
class Article(APIView):
  def get(self,request):
    res={"code":0}
    all_article = models.Article.object.all()                        #显示超链接的URL需要的参数,表示上下文
    ser_obj = app01_serializers.ArticleHyperLinkedSerializer(all_article, many=True, context={'request': request})
    res["data"] = ser_obj.data
      return Response(res)

  def post(self, request):
      res = {"code": 0}
      ser_obj = app01_serializers.ArticleModelSerializer(data=self.request.data)
      if ser_obj.is_valid():
          ser_obj.save()
      else:
          res["code"] = 1
          res["error"] = ser_obj.errors
      return Response(res)

# 文章详情CBV
class ArticleDetail(APIView):
    def get(self, request, pk):
        res = {"code": 0}
        article_obj = models.Article.objects.filter(pk=pk).first()
        # 序列化
        ser_obj = app01_serializers.ArticleHyperLinkedSerializer(article_obj, context={'request': request})
        res["data"] = ser_obj.data
        return Response(res)


# 学校详情CBV
class SchoolDetail(APIView):
    def get(self, request, id):
        res = {"code": 0}
        school_obj = models.School.objects.filter(pk=id).first()
        ser_obj = app01_serializers.SchoolSerializer(school_obj, context={'request': request})
        res["data"] = ser_obj.data
        return Response(res)

 

Response: 

   

配置之后:浏览器才可访问; rest_framework django的一个app

可发 get / post 请求 !!

 

rest framework 引入了Response对象,它是一个TemplateResponse类型,并根据客户端需求正确返回需要的类型。

 

使用前,需要导入模块

 

from rest_framework.response import Response

 

 

 

语法:

 

return Response(data) # 根据客户端的需求返回不同的类型。

 

原文地址:https://www.cnblogs.com/Mr-Murray/p/9402305.html