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 @ 2020-07-09 16:48  hanfe1  阅读(263)  评论(0编辑  收藏  举报