restful规范与rest_framework

django-restformwork

REST Restful

  • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
  • REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
  • 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
  • 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

Restful API设计规范

  • API与用户的通信协议,总是使用HTTPs协议

  • 域名

  • 版本

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

  • method

    • GET :从服务器取出资源(一项或多项)
    • POST :在服务器新建一个资源
    • PUT :在服务器更新资源(客户端提供改变后的完整资源)
    • PATCH :在服务器更新资源(客户端提供改变的属性)
    • DELETE :从服务器删除资源
  • 过滤,通过在url上传参的形式传递搜索条件

  • 状态码

    img

    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"
    }}

  摘自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html

序列化器的作用:

1.进行数据的校验

2.对数据对象进行转换,帮助我们进行序列化, 反序列化

序列化: 模型类对象 -----> python字典 用于输出, 返回给前端使用

反序列化: 前端传送的数据 -------> 经过验证 -----> python的字典 用于输入 接受前端数据时使用

序列化器作用: 帮助我们进行序列化, 反序列化

总结 :

在开发REST API接口时,我们在视图中需要做的最核心的事是:

将数据库数据序列化为前端所需要的格式,并返回;

将前端发送的数据反序列化为模型类对象,并保存到数据库中

在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本套路化,所以这部分代码也是可以复用简化编写的:

增:校验请求数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回

删:判断要删除的数据是否存在 -> 执行数据库删除

改:判断要修改的数据是否存在 -> 校验请求的数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回

查:查询数据库 -> 将数据序列化并返回

searlizier

可比作form

1.序列化的定义

根据模型类定义序列化器

示例:

#models.py
class Publisher(models.Model):
name = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=32)
pub_date = models.DateField()
category_choice = ((1, '言情'), (2, '都市'), (3, '科幻'))
category = models.IntegerField(choices=category_choice)
publisher = models.ForeignKey('Publisher', on_delete=models.CASCADE)
authors = models.ManyToManyField('Author')
2. 创建serializer对象

定义好Serializer类后,就可以创建Serializer对象了。

Serializer的构造方法为:

Serializer(instance=None, data=empty, **kwarg)

说明:

1)用于序列化时,将模型类对象传入instance参数

2)用于反序列化时,获取数据前进行验证 ( is_valid() ), 将要被反序列化的数据传入data参数

3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

serializer = AccountSerializer(account, context={'request': request})

通过context参数附加的数据,可以通过Serializer对象的context属性获取。

3. 序列化的使用
#serializers.py中写入
class PublisherSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
class AuthorSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
class BookSerializer(serializers.Serializer): #需要自定义update/create方法
id = serializers.IntegerField(required=False)
title = serializers.CharField(validators=[check_title])
pub_date = serializers.DateField()
category = serializers.CharField(source='get_category_display', read_only=True)
# book_obj.category book_obj.get_category_display()
publisher = PublisherSerializer(read_only=True)
authors = AuthorSerializer(many=True, read_only=True)
post_category = serializers.CharField(write_only=True)
post_publisher = serializers.IntegerField(write_only=True)
post_authors = serializers.ListField(write_only=True, )
def validate_title(self, attrs):
"""局部钩子"""
if '人妖' in attrs:
raise serializers.ValidationError('不合法')
else:
return attrs
def validate(self, attrs):
"""全局钩子"""
# 通过校验返回所有数据
# 不通过校验盘抛出异常
def create(self, validated_data):
"""新建"""
print(validated_data)
book_obj = models.Book.objects.create(
title=validated_data['title'],
category=validated_data['post_category'],
pub_date=validated_data['pub_date'],
publisher_id=validated_data['post_publisher'],
)
book_obj.authors.set(validated_data['post_authors'])
return book_obj
def update(self, instance, validated_data):
"""更新,instance为要更新的对象实例"""
instance.title = validated_data.get('title', instance.title)
instance.category = validated_data.get('post_category', instance.category)
instance.pub_date = validated_data.get('pub_date', instance.pub_date)
instance.publisher_id = validated_data.get('post_publisher', instance.publisher_id)
instance.save()
instance.authors.set(validated_data.get('post_authors', instance.authors.all()))
return instance
#views.py中
class BookListView(APIView):
"""使用djangorestful进行json序列化"""
queryset = models.Book.objects.all()
serializer_class = BookSerializer
def get(self, request):
"""以json形式返回书籍的列表"""
# 1. 获取所有的书籍对象
queryset = self.queryset
# 2. 将数据序列化成json格式
ser_obj = self.serializer_class(queryset, many=True)
# 3. 返回
return Response(ser_obj.data)
def post(self, request):
# 1.获取提交的数据
# print(request.data)
# request 是重新封装的对象 request._request ——》 原来的request对象
ser_obj = self.serializer_class(data=request.data)
# 2. 检验通过保存到数据库
if ser_obj.is_valid():
# ser_obj.validated_data
ser_obj.save()
return Response(ser_obj.data)
# 3. 返回不同的内容
return Response(ser_obj.errors)
class BookDetailView(APIView):
def get(self, request, pk):
"""获取一本书的详情"""
# 1. 根据PK获取一本书的对象
book_obj = models.Book.objects.filter(pk=pk).first()
if book_obj:
# 2. 对书的对象进行json序列化
ser_obj = BookSerializer(book_obj)
# 3. 返回json数据
return Response(ser_obj.data)
else:
return Response({'error': '查无此书'})
def put(self, request, pk):
"""修改一本书"""
# 1. 获取书籍对象
book_obj = models.Book.objects.filter(pk=pk).first()
# 2. 使用序列化器对数据进行校验 保存
ser_obj = BookSerializer(data=request.data, instance=book_obj, partial=True)
if ser_obj.is_valid():
ser_obj.save()
# 3. 返回修改后的对象的json数据
return Response(ser_obj.data)
else:
return Response(ser_obj.errors)
def delete(self, request, pk):
"""删除一本书"""
# 1. 获取书籍对象
book_obj = models.Book.objects.filter(pk=pk).first()
if book_obj:
# 2. 删除
book_obj.delete()
# 3. 返回json数据
return Response({'msg': '删除成功'})
else:
return Response({'error': '查无此书'})

1.获取序列化数据,通过data属性可以获取序列化后的数据

2.如果要被序列化的是包含多条数据的查询集QuerySet,可以通过添加many=True参数补充说明

3.包含read_only=True参数时,字段用作序列化(get)使用,write_only=True,用于反序列化新增post等操作。

4. 反序列化的使用

1.验证

在获取反序列化的数据前,必须调用is_valid()方法进行验证,验证成功返回True,否则返回False。

验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。

验证成功,可以通过序列化器对象的validated_data属性获取数据。

is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。

## Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

2.保存

如果在验证成功后,想要基于validated_data完成数据对象的创建,可以通过实现create()和update()两个方法来实现。

如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用,相反,如果传递了instance实例,则调用save()方法的时候,update()被调用。

两点说明:

1) 在对序列化器进行save()保存时,可以额外传递数据,这些数据可以在create()和update()中的validated_data参数获取到

2)默认序列化器必须传递所有required的字段,否则会抛出验证异常。但是我们可以使用partial参数来允许部分字段更新

modelsearlizer

可比作modelform

ModelSerializer与常规的Serializer相同,但提供了:

基于模型类自动生成一系列字段 # 自动生成字段

基于模型类自动为Serializer生成validators,比如unique_together # 唯一约束

包含默认的create()和update()的实现 # 默认的两种方法

#示例:
class PublisherModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publisher
fields = '__all__'
class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Author
fields = '__all__'
class BookModelSerializer(serializers.ModelSerializer):
category_info = serializers.SerializerMethodField() # 找 get_字段名的方法 执行
publisher_info = serializers.SerializerMethodField()
author_info = serializers.SerializerMethodField()
def get_category_info(self, obj):
return obj.get_category_display()
def get_publisher_info(self, obj):
# ret = {
# 'id' : obj.publisher_id,
# 'name':obj.publisher.name,
# }
# return ret
ser_obj = PublisherSerializer(obj.publisher) #返回序列化后的数据
return ser_obj.data
def get_author_info(self, obj):
ser_obj = AuthorSerializer(obj.authors.all(), many=True)
return ser_obj.data
class Meta:# Meta是一个内部类,它用于定义一些Django模型类的行为特性
model = models.Book
fields = '__all__'
# depth = 1 # 跟对象关联的内容 属性read_only = True
# exclude = [] #排除哪些参数
extra_kwargs = { #额外的参数
'category': {'write_only': True, },
'publisher': {'write_only': True},
'authors': {'write_only': True},
}
#read_only_fields = ('','') #只读字段

model 指明参照哪个模型类

fields 指明为模型类的哪些字段生成

视图view:

普通view:

路由:写的 2个url

视图优化1:将重复的类提到基类 (自己写的)

将功能拆分开,单独写成类。

#通用类
class GenericView(APIView):
queryset = None #查询的数据
serializer_class = None #序列化起的类(需要自己写)
def get_queryset(self):
# 确保每次查询都是最新的数据
return self.queryset.all()
def get_obj(self, request, pk, *args, **kwargs):
return self.get_queryset().filter(pk=pk).first()
#展示列表
class ListViewMixin(object): # 混合类 不能单独使用 配合其他的类进行使用(利用Python的多继承)
def list(self, request):
queryset = self.get_queryset()
ser_obj = self.serializer_class(queryset, many=True)
return Response(ser_obj.data)
# 新增数据
class CreateViewMixin(object):
def create(self, request):
ser_obj = self.serializer_class(data=request.data)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.data)
return Response(ser_obj.errors)
# 获取一个数据
class RetrieveViewMixin(object):
def retrieve(self, request, pk):
obj = self.get_object()
if obj:
ser_obj = self.serializer_class(obj)
return Response(ser_obj.data)
else:
return Response({'error': '查无此ID'})
# 更新
class UpdateViewMixin(object):
def update(self, request, pk):
obj = self.get_object()
ser_obj = self.serializer_class(data=request.data, instance=obj, partial=True)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.data)
else:
return Response(ser_obj.errors)
# 删除
class DestroyViewMixin(object):
def destroy(self, request, pk):
obj = self.get_object()
if obj:
obj.delete()
return Response({'msg': '删除成功'})
else:
return Response({'error': '查无此ID'})
#这样使用就方便很多,功能拆分
class PublisherListView(GenericView, ListViewMixin, CreateViewMixin):
queryset = models.Publisher.objects.all()
serializer_class = PublisherModelSerializer
class PublisherDetialView(GenericView, RetrieveViewMixin, UpdateViewMixin, DestroyViewMixin):
queryset = models.Publisher.objects.all()
serializer_class = PublisherModelSerializer

视图优化2:drf框架中generics已经写好,上面的视图就不用自己再写了(以后用这个就行)

view: 如果用这种方法,注意需要在url中用(?P\d+)传递参数,具体定义的参数可在GenericAPIView中lookup_field = 'pk'查看,也可在子类中重写这个属性。

'''
GenericAPIView:通用 #
ListAPIView:展示所有数据
CreateAPIView:新增数据
DestroyAPIView:删除一个数据(pk)
RetrieveAPIView:展示一个数据(pk)
UpdateAPIView:更新一个数据(pk)
'''
from rest_framework.generics import GenericAPIView,ListAPIView,CreateAPIView,DestroyAPIView,RetrieveAPIView,UpdateAPIView
from rest_framework.generics import GenericAPIView,ListCreateAPIView,RetrieveUpdateDestroyAPIView #封装的上面的类,用的时候不用引用那么多类了。

序列化器:

from rest_framework import serializers
from book import models
class PublisherModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publisher
fields = '__all__'
class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Author
fields = '__all__'
class BookSerializer(serializers.ModelSerializer):
category_info = serializers.SerializerMethodField() # 执行 get_字段名的方法
publisher_info = serializers.SerializerMethodField() #
authors_info = serializers.SerializerMethodField() #
def get_category_info(self, obj):
return obj.get_category_display()
def get_publisher_info(self, obj):
return PublisherModelSerializer(obj.publisher).data
def get_authors_info(self, obj):
return AuthorModelSerializer(obj.authors.all(), many=True).data
class Meta:
model = models.Book
fields = '__all__'
extra_kwargs = {
'category': {'write_only': True},
'publisher': {'write_only': True, },
'authors': {'write_only': True, 'required': False}
}

视图:

from rest_framework.generics import GenericAPIView,ListCreateAPIView,RetrieveUpdateDestroyAPIView
from book import models
from book.serializer import BookSerializer
Class BookListView(ListCreateAPIView):
queryset= models.Book.objects.all()
serializer_class = BookSerializer
Class BookDetailView(RetrieveUpdateDestroyAPIView):
queryset= models.Book.objects.all()
serializer_class = BookSerializer
lookup_field = 'pk' #可以重写pk的

drf的viewset:视图集合

路由:2个url,根据action定义的方法(list,retrieve)进行分发。

视图:写一个视图即可(不用分取单个数据的视图了)

modelviewset:

#源码:
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass

也可以这么写,modelviewset中继承类了你所需要的类,会根据你url上写的action,去执行相应的函数。

简洁写法url,自动生成2个url,带pk的(不推荐,不直观)

view-modelviewset视图梳理:

继承类补充:

都是从最底层的继承顺序开始查找,自己找不到,才会去继承的类中找。

深度优先:旧式类

广度优先:新式类都是广度优先(在菱形继承中才有用),普通的继承还是深度优先。

例如:

认证:

权限:

限制频率:

Django Rest Framework官网:https://www.django-rest-framework.org/api-guide/requests/#requests

参考:大佬博客
Django Rest Framework: https://www.cnblogs.com/maple-shaw/p/7865767.html
Django Rest Framework-APIView源码分析:https://www.cnblogs.com/maple-shaw/p/7874900.html

https://www.cnblogs.com/dongxixi/p/11116899.html

posted @   hanfe1  阅读(269)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示