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协议。
-
域名
- https://api.example.com 尽量将API部署在专用域名(会存在跨域问题)
- https://example.org/api/ API很简单
-
版本
- URL,如:https://api.example.com/v1/
- 请求头 跨域时,引发发送多次请求
-
路径,视网络上任何东西都是资源,均使用名词表示(可复数)
-
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" }}
摘自: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
''' 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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)