赞
踩
在开发web应用中,有两种应用模式:前后端不分离和前后端分离。
前后端不分离
前后端分离
前后端分离开发示例
获取图书数据 Web API
- 请求方式:get
- 请求路径:book/1
- 请求参数:图书id 1
- 请求结果:json {‘books’:[{‘name’:‘西游记’,‘bread’:20},{‘name’:‘三国’,‘bread’:30}]}
前端
<ul> <li v-for="book in data"> {{book.name}} {{book.bread}} </li> </ul> axios.get('http:\\127.0.0.1:8000/book/1') # 200系列状态码,执行then .then(response => { this.data = response.data.books }) # 返回400/500状态码,执行catch .catch(response = >{ response.data.error })
后端
url(r'book/(?P<pk>\d+)',views.BookView.as_view())
class BookView(View):
def get(self,request,pk):
# 1.获取数据
# 2.查询数据
try:
book = Book.object.get(id=pk)
except:
return HttpResponse()
data = {"name":book.name, "bread":book.bread}
return JsonResponse(data)
在前后端分离的应用模式里,API接口普遍采用RESTful设计风格
1.域名:应该尽量将API部署在专有域名之下
https://api.example.com
如京东:https://shouji.jd.com/
如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下
https:example.org/api/
2.版本:应该将API的版本号放入url
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo
另一种做法是将版本号放在HTTP头信息中,因为不同的版本可以理解成同一种资源的不同表现形式,所以应该采用同一种url,版本号可以放在HTTP请求头信息的Accept字段中进行区分。
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0
3.路径
路径又称“终点”,表示API的具体网址,每个网址代表一种资源。
4.HTTP动词
对于资源的具体操作类型,由HTTP动词表示,常用的HTTP动词有下面四个。
GET:从服务器取出资源(一项或多项)。
POST:在服务器新建一个资源。
PUT:在服务器更新资源(客户端提供改变后的完整资源)。
DELETE:从服务器删除资源。
如:
GET /zoos:列出所有动物园
POST /zoos:新建一个动物园(上传文件)
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
5.过滤信息以查询字符串进行
如果记录数量很多,API应该提供参数,过滤返回结果。
下面是一些常见的参数:
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定
6.状态码
后端执行成功或失败以状态码的形式告诉前端,处理成功返回200系列状态码,执行前端then里面的代码;处理失败返回400/500系列状态码,执行catch里面的代码。
- 200 OK :服务器成功返回用户请求的数据
- 201 CREATED :用户新建或修改数据成功。
- 202 Accepted :表示一个请求已经进入后台排队(异步任务)
- 204 NO CONTENT :用户删除数据成功。
- 400 INVALID REQUEST :用户发出的请求有错误,服务器没有进行新建或修改数据的操作
- 401 Unauthorized :表示用户没有权限(令牌、用户名、密码错误)。
- 403 Forbidden :表示用户得到授权(与401错误相对),但是访问是被禁止的。
- 404 NOT FOUND :用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
- 406 Not Acceptable :用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
- 410 Gone:用户请求的资源被永久删除,且不会再得到的。
- 422 Unprocesable entity : 当创建一个对象时,发生一个验证错误。
- 500 INTERNAL SERVER ERROR :服务器发生错误,用户将无法判断发出的请求是否成功
{
error:"出错信息"
}
使用图书英雄案例来写一套支持图书数据增删改查的REST API接口
① 接口设计
请求方式 | 请求路径 | 请求参数 | 返回结果 |
---|---|---|---|
POST | books/ | title、bpub_date (请求体json) | 保存后的图书数据books=[{btitle:“西游记”,bpub_date:2017},{…}] |
请求方式 | 请求路径 | 请求参数 | 返回结果 |
---|---|---|---|
DELETE | books/1 | id (路径) |
请求方式 | 请求路径 | 请求参数 | 返回结果 |
---|---|---|---|
PUT | books/1 | 修改字段内容(json)、id (路径) | 更新后的图书数据 book={ btitle:“xxx”,bpub_date:“xxx”} |
查询单一图书
请求方式 | 请求路径 | 请求参数 | 返回结果 |
---|---|---|---|
GET | books/1 | id (路径) | book={ btitle:“xxx”,bpub_date:“xxx”} |
查询所有图书
请求方式 | 请求路径 | 请求参数 | 返回结果 |
---|---|---|---|
GET | books/1 | books=[{btitle:“西游记”,bpub_date:2017},{…}] |
② 后端代码实现
urlpatterns = [ url(r'^books/$', views.BooksView.as_view()), url(r'^books/(?P<pk>\d+)/', views.BookView.as_view()), ] from django.views import View from django.http import JsonResponse from book.models import BookInfo import json class BooksView(View): def get(self,request): """获取所有图书"""" books = BookInfo.objects.all() data = [] for book in books: data.append({ "id": book.id, "btitle": book.btitle, "bpub_date": book.bpub_date }) # safe=False,是将列表[]转成json中的数组"[]" return JsonResponse(data, safe=False) def post(self,request): """保存图书""" # 1、获取前端数据 data = request.body.decode() data_dict = json.loads(data) btitle = data_dict.get("btitle") bpub_date = data_dict.get("bpub_date") # 2、验证数据 if btitle == "python": return JsonResponse({"error": "错误的书名"},status=400) # 3、保存数据 book = BookInfo.objects.create(btitle=btitle, bpub_date=bpub_date) # 4、返回结果 return JsonResponse({ "id": book.id, "bpub_date": book.bpub_date, "btitle": book.btitle }) class BookView(View): def get(self,request,pk): """查询一本图书""" try: book = BookInfo.objects.get(id=pk) except: return JsonResponse({ "id": book.id, "btitle": book.btitle, "bpub_date": book.bpub_date }) def put(self,request,pk): """修改一本图书""" # 1、获取数据 data = request.body.decode() data_dict = json.loads(data) btitle = data_dict.get("btitle") bpub_date = data_dict.get("bpub_date") # 2、校验数据 # 3、更新数据 # 这种方法得到的并不是对象,而是得到更新数据的个数 # book = BookInfo.objects.filter(id=pk).update(btitle=btitle,bpub_date=bpub_date) try: book = BookInfo.objects.get(id=pk) except: return JsonResponse({"error": "错误的id"},status=400) book.btitle = btitle book.bpub_date = bpub_date book.save() # 4、返回结果 return JsonResponse({ "id": book.id, "btitle": book.btitle, "bpub_date": book.bpub_date }) def delete(self,request,pk): """删除一本图书""" try: book = BookInfo.objects.get(id=pk) except: return JsonResponse({"error": "错误的id"}, status=400) book.delete()
pip install djangorestframework
INSTALLED_APPS = [
...
'rest_framework',
]
序列化器的作用:
在应用的目录下创建serializers.py文件,Serializer使用类来定义,须继承自rest_framework.serializers.Serializer.
from rest_framework import serializers
class BookSerializer(serializers.Serializers):
# 根据模型类字段进行定义字段
btitle=serializers.CharField()
bpub_date=serializers.DateField()
注:serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义,serializer是独立于数据库之外的存在。
from book.serializers import BookSerializer
class BookView(View):
def get(self,request,pk):
"""查询一本图书"""
try:
# 查询出一个图书对象
book = BookInfo.objects.get(id=pk)
except:
# return JsonResponse({"id": book.id,"btitle": book.btitle,"bpub_date": book.bpub_date})
# 1.构造序列化器对象
serializer = BookSerializer(book)
# 2.通过data方法获取序列化数据
data = serializer.data
return JsonResponse(data)
from book.serializers import BookSerializer
class BooksView(View):
def get(self,request):
"""获取所有图书""""
books = BookInfo.objects.all()
# data = []
# for book in books:
# data.append({"id": book.id,"btitle": book.btitle,"bpub_date": book.bpub_date})
serializer = BookSerializer(books,many=True)
return JsonResponse(serializer.data, safe=False)
heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True,many=True)
{"btitle":"天龙八部", "bpub_date": "1986-07-24", "heroinfo_set":[6,7,8,9]}
heroinfo_set = serializers.StringRelatedField(read_only=True,many=True)
{"btitle":"天龙八部", "bpub_date": "1986-07-24", "heroinfo_set":["乔峰", "段誉","虚竹","王语嫣"]}
heroinfo_set = HeroSerializer(many=True)
{
"btitle":"天龙八部",
"bpub_date": "1986-07-24",
"heroinfo_set":[
{"hname":"乔峰","hgender":1},
{"hname": "段誉","hgender":1},
{"hname":"虚竹","hgender":1},
{"hname":"王语嫣","hgender":0)
]
}
例如:定义图书数据的序列化器时,关联对象英雄的序列化
from rest_framework import serializers
class HeroSerializer(serializers.Serializer):
hname = serializers.CharField()
hgender = serializers.IntegerField()
class BookSerializer(serializers.Serializers):
# 根据模型类字段进行定义字段
btitle=serializers.CharField()
bpub_date=serializers.DateField()
# heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True,many=True)
# heroinfo_set = serializers.StringRelatedField(read_only=True,many=True)
heroinfo_set = HeroSerializer(many=True)
参数名称 | 作用 |
---|---|
max_length | 最大长度 |
min_length | 最小长度 |
allow_blank | 是否允许为空 |
max_value | 最大值 |
min_value | 最小值 |
通用参数
参数名称 | 说明 |
---|---|
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
default | 反序列化时使用的默认值 |
calss BookSerializer(serializers.Serializaer):
btitle = serializers.CharField(max_length=11, min_length=5)
bpub_date = serializers.DateField(write_onlt=True)
bread = serializers.IntegerField(read_only=True)
bcomment = serializers.IntegerField(default=2, min_value=1, max_value=60)
通过构造序列化器对象,并将要反序列化的数据传递给data构造参数,进而进行验证
from django.views import View from django.http import JsonResponse from book.models import BookInfo import json class BooksView(View): def post(self,request): """保存图书""" # 1、获取前端数据 data = request.body.decode() data_dict = json.loads(data) # btitle = data_dict.get("btitle") # bpub_date = data_dict.get("bpub_date") # 2、验证数据 ser = BookSerializer(data=data_dict) ser.is_valid(raise_exception=True) print(ser.errors) # 字典 {'btitle': [ErrorDetail(string='This field is required.', code='required')]} # 3、保存数据 # 4、返回结果
说明:
raise_exception=True
参数开启,is_valid()方法会在验证失败时抛出异常,向前端返回400 Bad Request响应。
自定义验证:
在serializers.py文件对应的序列化器中定义验证方法,is_valid()对字段验证没有问题后,会调用自定义的验证方法
from rest_framework import serializers
calss BookSerializer(serializers.Serializaer):
btitle = serializers.CharField(max_length=11, min_length=5)
bpub_date = serializers.DateField(write_onlt=True)
bread = serializers.IntegerField(read_only=True)
bcomment = serializers.IntegerField(default=2, min_value=1, max_value=60)
# 自定义单一字段验证
def validate_btitle(self, value):
if value == "python":
raise serializers.ValidationError("书名python错误")
return value
calss BookSerializer(serializers.Serializaer):
btitle = serializers.CharField(max_length=11, min_length=5)
bpub_date = serializers.DateField(write_onlt=True)
bread = serializers.IntegerField(read_only=True)
bcomment = serializers.IntegerField(default=2, min_value=1, max_value=60)
# 自定义多个字段验证
def validate(self, attrs):
if attrs["btitle"] == "java":
raise serializers.ValidationError("书名java错误")
return attrs
from rest_framework import serializers calss BookSerializer(serializers.Serializaer): btitle = serializers.CharField(max_length=11, min_length=5) bpub_date = serializers.DateField(write_onlt=True) bread = serializers.IntegerField(read_only=True) bcomment = serializers.IntegerField(default=2, min_value=1, max_value=60) # 自定义单一字段验证 def validate_btitle(self, value): if value == "python": raise serializers.ValidationError("书名python错误") return value # 自定义多个字段验证 def validate(self, attrs): if attrs["btitle"] == "java": raise serializers.ValidationError("书名java错误") return attrs # 保存数据 def create(self, validated_data): book = BookInfo.objects.create(btitle=validated_data["btitle"],bpub_date=validated_data["bpub_date"]) return book # 更新数据 def update(self, instance, validated_data): # instance 要更新的数据对象 instance.btitle = validated_data['btitle'] instance.save() return instance
视图 views.py
from django.views import View from django.http import JsonResponse from book.models import BookInfo import json class BooksView(View): def post(self,request): """保存图书""" # 1、获取前端数据 data = request.body.decode() data_dict = json.loads(data) # btitle = data_dict.get("btitle") # bpub_date = data_dict.get("bpub_date") # 2、验证数据 ser = BookSerializer(data=data_dict) ser.is_valid(raise_exception=True) # 3、保存数据 # book BookInfo.objects.create(btitle=ser.validated_data['btitle'], # bpub_date=ser.validated_data["bpub_date"]) ser.save() # 4、返回结果 return JsonResponse(ser.data) class BookView(View): def put(self,request,pk): """修改一本图书""" # 1、获取数据 data = request.body.decode() data_dict = json.loads(data) # 2、校验数据 book=BookInfo.objects.get(id=pk) ser = BookSerializer(book, data=data_dict) ser.isvalid(raise_exception=True) # 3、更新数据 ser.save() # 4、返回结果 return JsonResponse(ser.data)
说明:
如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。
ModelSerializer与常规的Serializer相同,但提供了:
① 基于模型类自动生成一系列字段
② 基于模型类自动为Serializer生成validators,比如unique_together
③ 包含默认的create()和update()的实现
class BookModelSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
fields = '__all__'
views.py
from django.views import View from django.http import JsonResponse from book.models import BookInfo from book.serializers import BookModelSerializer import json class BooksView(View): def get(self,request): """获取所有图书"""" books = BookInfo.objects.all() ser = BookModelSerializer(books, many=True) return JsonResponse(ser.data, safe=False)
① 使用fields来明确字段,__all__表名包含所有字段,也可以写明具体哪些字段
class BookModelSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
fields = ("id", "btitle")
② 使用exclude可以明确排除掉哪些字段
class BookModelSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
exclude = ("id","btitle",)
③ 指明只读字段
class BookModelSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
fields = '__all__'
read_only_fields = ("bread",)
④ 显示指明字段
class BookModelSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
# 显示指明字段
bcomment = serializers.IntegerField(max_value=1000,min_value=50)
class Meta:
model = BookInfo
fields = '__all__'
read_only_fields = ("bread",)
class BookModelSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
bcomment = serializers.IntegerField(max_value=1000,min_value=50)
class Meta:
model = BookInfo
fields = '__all__'
read_only_fields = ("bread",)
# 使用extra_kwargs参数为is_delete字段添加默认值选项
extra_kwargs={"is_delete":{"default":True}}
1.Request
from rest_framework.request import Request
REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的拓展了HttpRequest类的Request类的对象。
REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型将请求数据进行parse解析,解析为类字典对象保存到Request对象中。
Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。
常用属性:
request.data
返回解析之后的请求体数据,与Django中标准的request.POST 和 request.FILES属性,但提供如下特性:
request.query_params
与Django标准的request.GET相同Response
from rest_framework.response import Response
REST framework提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据内容会被转换成符合前端需求的类型。
3.1 两个基类
1.APIView
rest_framework.views.APIView
APIView是framework提供的所有视图的基类,继承自Django的View父类
APIView与View的不同之处在于:
支持定义的属性:
from book.models import BookInfo from book.serializers import BookSerializer from rest_framework.response import Response from rest_framework.views import APIView class BooksView(APIView): def get(self,request): """获取所有图书""" books = BookInfo.objects.all() ser = BookSerializer(books, many=True) return Response(ser.data) def post(self,request): """保存图书""" # 1、获取前端数据 data = request.data # 2、验证数据 ser = BookSerializer(data=data) ser.is_valid(raise_exception=True) # 3、保存数据 ser.save() # 4、返回结果 return Response(ser.data) class BookView(APIView): def get(self, request, pk): """获取单一图书""" # 查询数据 try: hero = BookInfo.objects.get(id=pk) except: return Response({"error":"错误的id"}, status=400) # 返回结果 ser = BookSerializer(book) return Response(ser.data) def put(self,request,pk): """修改一本图书""" # 1、获取数据 data = request.data # 2、校验数据 book=BookInfo.objects.get(id=pk) ser = BookSerializer(book, data=data) ser.isvalid(raise_exception=True) # 3、更新数据 ser.save() # 4、返回结果 return Response(ser.data)
rest_framework.generics.CenericAPIView
from book.models import BookInfo from book.serializers import BookSerializer from rest_framework.response import Response from rest_framework.views import GenericAPIView class BooksView(GenericAPIView): serializer_class = BookSerializer # 指定序列化器 queryset = BookInfo.objects.all() # 指定数据对象查询集 def get(self,request): """获取所有图书""" books = self.get_queryset() # 获取查询集所有数据 ser = self.get_serializer(books, many=True) # 获取序列化器对象 return Response(ser.data) def post(self,request): """保存图书""" # 1、获取前端数据 data = request.data # 2、验证数据 ser = self.get_serializer(data=data) ser.is_valid(raise_exception=True) # 3、保存数据 ser.save() # 4、返回结果 return Response(ser.data) class BookView(GenericAPIView): serializer_class = BookSerializer # 指定序列化器 queryset = BookInfo.objects.all() # 指定数据对象查询集 def get(self, request, pk): """获取单一图书""" # 查询数据 try: book = self.get_object() # 从查询集中获取单一数据对象 except: return Response({"error":"错误的id"}, status=400) # 返回结果 ser = self.get_serializer(book) return Response(ser.data) def put(self,request,pk): """修改一本图书""" # 1、获取数据 data = request.data # 2、校验数据 book= self.get_object() ser = self.get_serializer(book, data=data) ser.isvalid(raise_exception=True) # 3、更新数据 ser.save() # 4、返回结果 return Response(ser.data)
3.2 五个拓展类
1.ListModelMixin
列表视图拓展类,提供list(request, *args, **kwargs)
方法快速实现列表视图,该list()方法会对数据进行过滤和分页。成功返回200状态码
2.CreateModelMixin
创建视图拓展类,提供create(request, *args, **kwargs)
方法快速实现创建资源的视图,成功返回201状态码,失败返回400错误。
3.RetrieveModelMixin
详情视图拓展类,提供retrieve(request, *args, **kwargs)
方法,可以快速实现返回一个存在的数据对象。如果存在,返回200;否则返回404。
4.UpdateModelMixin
更新视图拓展类,提供update(request, *args, **kwargs)
方法,可以快速实现更新一个存在的数据对象。成功返回200,失败返回400。
5.DestoryModelMixin
删除视图拓展类,提供destroy(request, *args, **kwargs)
方法,可以快速实现删除一个存在的数据对象。成功返回204,不存在返回404。
from book.models import BookInfo from book.serializers import BookSerializer from rest_framework.response import Response from rest_framework.views import GenericAPIView from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin, ListModelMixin class BooksView(GenericAPIView,CreateModelMixin, ListModelMixin): serializer_class = BookSerializer # 指定序列化器 queryset = BookInfo.objects.all() # 指定数据对象查询集 def get(self, request): """获取所有图书""" return self.list(request) def post(self, request): """保存图书""" return self.create(request) class BookView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin): serializer_class = BookSerializer # 指定序列化器 queryset = BookInfo.objects.all() # 指定数据对象查询集 def get(self, request, pk): """获取单一图书""" return self.retrieve(request, pk) def put(self, request, pk): """修改一本图书""" return self.update(request, pk)
总结:
调用流程:前端发起请求--->自己定义的类视图--->拓展类方法--->GenericAPIView方法
3.3 拓展类子类视图
CreateAPIView
ListAPIView
RetrieveAPIView
DestroyAPIView
UpdateAPIView
RetrieveUpdateAPIView
RetrieveUpdateDestoryAPIView
ListCreateAPIView
RetrieveDestroyAPIView
from rest_framework.generics import CreateAPIView, ListAPIView, RetrieveAPIView, DestroyAPIView, UpdateAPIView, ListCreateAPIView, RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView, RetrieveUpdateAPIView
from book.models import BookInfo
from book.serializers import BookSerializer
class BooksView(ListCreateAPIView):
serializer_class = BookSerializer # 指定序列化器
queryset = BookInfo.objects.all() # 指定数据对象查询集
class BookView(RetrieveUpdateAPIView):
serializer_class = BookSerializer # 指定序列化器
queryset = BookInfo.objects.all() # 指定数据对象查询集
1、两个基本的视图集:
ViewSet
urlpatterns = {
# 视图集路由匹配 ViewSet
url(r'^books$', BooksViewSet.as_view({"get": "list", "post": "create"})),
url(r'^books/last/$', BooksViewSet.as_view({"get": "last"})),
url(r'^books/(?P<pk>\d+)/show/$',BooksViewSet.as_view({"get": "show"})),
}
from rest_framework.viewsets import ViewSet from book.models import BookInfo from books.serializers import BookSerializer from rest_framework.response import Response class BooksViewSet(ViewSet): def list(self, request): """返回所有图书数据""" books = BookInfo.objects.all() ser = BookSerializer(books, many=True) return Response(ser.data) def last(self, request): """返回一本图书信息""" book = BookInfo.objects.get(id=10) ser = BookSerializer(book) return Response(ser.data) def show(self, request, pk): """返回前端指定的图书信息""" book = BookInfo.objects.get(id=pk) ser = BookSerializer(book) return Response(ser.data) def create(self, request): """保存图书数据""" data = request.data ser = BookSerializer(data=data) ser.is_valid(raise_exception=True) ser.save() return Response(ser.data)
GenericViewSet
urlpatterns = {
# 视图集路由匹配 GenericViewSet
url(r'^books$', BooksGenericViewSet.as_view({"get": "list", "post": "create"})),
url(r'^books/last/$', BooksGenericViewSet.as_view({"get": "last"})),
url(r'^books/(?P<pk>\d+)/show/$', BooksGenericViewSet.as_view({"get": "show"})),
}
class BooksGenericViewSet(GenericViewSet): serializer_class = BookSerializer queryset = BookInfo.objects.all() def list(self, request): """返回所有图书数据""" books = self.get_queryset() ser = self.get_serializer(books, many=True) return Response(ser.data) def last(self, request): """返回一本图书信息""" book = BookInfo.objects.get(id=10) ser = self.get_serializer(book) return Response(ser.data) def show(self, request, pk): """返回前端指定的图书信息""" book = self.get_object() ser = self.get_serializer(book) return Response(ser.data) def create(self, request): """保存图书数据""" data = request.data ser = self.get_serializer(data=data) ser.is_valid(raise_exception=True) ser.save() return Response(ser.data)
2、两个拓展视图集
ModelViewSew
urlpatterns = {
# 视图集路由匹配 ModelViewSet
url(r'^books$', BooksModelViewSet.as_view({"get": "list", "post": "create"})),
url(r'^books/(?P<pk>\d+)/$', BooksModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
url(r'^books/(?P<pk>\d+)/show/$', BooksModelViewSet.as_view({"get": "show"})),
}
from rest_framework.viewsets import ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.response import Response
class BooksModelViewSet(ModelViewSet):
serializer_class = BookSerializer
queryset = BookInfo.objects.all()
# 自定义方法
def show(self, request, pk):
book = self.get_object()
ser = self.get_serializer(book)
return Response(ser.data)
ReadOnlyModelViewSet
3、action属性
在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪一个。
class BooksModelViewSet(ModelViewSet):
# serializer_class = BookSerializer
queryset = BookInfo.objects.all()
# 对于不同的方法使用不同的序列化器
def get_serializer_class(self):
if self.action == "list":
return BookSerializer
elif self.action == "create":
return BookSerializer1
else:
return BookSerializer2
对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作之间的对应关系外,还可以使用Routers快速实现路由信息。
REST framework提供了两个router
DefaultRouter与SimpleRouter的区别:DefaultRouter继承自SimpleRouter,多提供了一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。
1、使用方法:
from rest_framework import routers
router = routers.SimpleRouter()
router.register(prefix, viewset, base_name)
说明:router.register(prefix, viewset, base_name)
prefix 该视图集的路由前缀
viewset 视图集
base_name 路由名称的前缀
第一种方式:
urlpatterns = [
...
]
urlpatterns += router.urls
第二种方式:
urlpatterns = [
...
url(r'^', include(router.urls))
]
示例:
# 自动生成路由的使用 from rest_framework.routers import SimpleRouter urlpatterns = [ ] router = SimpleRouter() router.register("books", BooksModelViewSet, base_name='book') urlpatterns += router.urls from rest_framework.viewsets import ViewSet, GenericViewSet, ModelViewSet from book.models import BookInfo from books.serializers import BookSerializer from rest_framework.response import Response class BooksModelViewSet(ModelViewSet): # serializer_class = BookSerializer queryset = BookInfo.objects.all() # 对于不同的方法使用不同的序列化器 def get_serializer_class(self): if self.action == "list": return BookSerializer elif self.action == "create": return BookSerializer1 else: return BookSerializer2 def show(self, request, pk): book = self.get_object() ser = self.get_serializer(book) return Response(ser.data)
此视图集会形成的路由:
[<RegexURLPattern book-list ^books/$>,<RegexURLPattern book-detail ^books/(?P<pk>[ ^/.]+)/ $>]
2、视图集中包含附加action装饰器的
对于自定义方法,自动生成路由,需要使用action装饰器
rest_framework.decorators.action
action装饰器接收两个参数:
示例:
# 自动生成路由的使用 from rest_framework.routers import SimpleRouter urlpatterns = [ ] router = SimpleRouter() router.register("books", BooksModelViewSet, base_name='book') urlpatterns += router.urls from rest_framework.viewsets import ViewSet, GenericViewSet, ModelViewSet from book.models import BookInfo from books.serializers import BookSerializer from rest_framework.response import Response from rest_framework.decorators import action class BooksModelViewSet(ModelViewSet): # serializer_class = BookSerializer queryset = BookInfo.objects.all() # 对于不同的方法使用不同的序列化器 def get_serializer_class(self): if self.action == "list": return BookSerializer elif self.action == "create": return BookSerializer1 else: return BookSerializer2 # 自定义方法 @action(methods=["get"], detail=True) def show(self, request, pk): book = self.get_object() ser = self.get_serializer(book) return Response(ser.data)
此视图集会形成的路由:
[<RegexURLPattern book-list ^books/$>,<RegexURLPattern book-detail ^books/(?P<pk>[ ^/.]+)/ $>,<RegexURLPattern book-show ^books/(?P<pk>[ ^/.]+)/show/ $>]
总结-视图集使用场景:
1.1 全局:在配置文件settins.py文件中配置全局默认的认证方案
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication', # 基本认证
'rest_framework.authentication.SessionAuthentication', # session认证
)
}
1.2 局部:在每个视图中通过authentication_classes属性设置
from rest_framework.viewsets import ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.authentication import SessionAuthentication, BaseAuthentication
class BooksModelViewSet(ModelViewSet):
serializer_class = BookSerializer
queryset = BookInfo.objects.all()
# 认证
authentication_classes = [BaseAuthentication, SessionAuthentication]
认证失败会有两种可能的返回值:
权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。
2.1 全局:在配置文件中设置默认的权限管理类
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', # 仅通过认证的用户
)
}
如果未指明,则采用如下配置
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny', # 允许所有用户
)
}
2.2 局部:在具体的视图中通过permission_classes属性设置
from rest_framework.viewsets import ModelViewSet
from book.models import BookInfo
from books.serializers import BookSerializer
from rest_framework.authentication import SessionAuthentication, BaseAuthentication
from rest_framework.permissions import IsAuthenticated
class BooksModelViewSet(ModelViewSet):
serializer_class = BookSerializer
queryset = BookInfo.objects.all()
# 认证
authentication_classes = [BaseAuthentication, SessionAuthentication]
# 权限
permission_classes = [IsAuthenticated]
提供的权限:
可以对接口访问的频次进行限制,以减轻服务器压力。
3.1 全局:配置文件中,使用DEFAULT_THROTTLE_CLASSES 和 DEFAULT_THROTTLE_RATES进行全局配置
REST_FRAMEWORK = {
# 用户划分限流
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
),
# 指定不同用户访问次数,可以使用 second, minute, hour 或day来指明周期。
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
3.2 局部:在具体视图中通过throttle_classes属性来配置
from rest_framework.viewsets import ModelViewSet from book.models import BookInfo from books.serializers import BookSerializer from rest_framework.authentication import SessionAuthentication, BaseAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.throttling import UserRateThrottle class BooksModelViewSet(ModelViewSet): serializer_class = BookSerializer queryset = BookInfo.objects.all() # 认证 authentication_classes = [BaseAuthentication, SessionAuthentication] # 权限 permission_classes = [IsAuthenticated] # 限流 throttle_classes = [UserRateThrottle]
可选限流类:
ScopedRateThrottle 示例:
class ContactListView(APIView):
throttle_scope = 'contacts'
...
class ContactDetailView(APIView):
throttle_scope = 'contacts'
...
class UploadView(APIView):
throttle_scope = 'uploads'
...
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.ScopedRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'contacts': '1000/day',
'uploads': '20/day'
}
}
对于列表数据可能需要根据字段进行过滤,可以通过添加django-filter扩展增强支持。
pip install django-filter
INSTALLED_APPS = [
...
'django_filters', # 需要注册应用,
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
from rest_framework.viewsets import ModelViewSet from book.models import BookInfo from books.serializers import BookSerializer from rest_framework.authentication import SessionAuthentication, BaseAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.throttling import UserRateThrottle class BooksModelViewSet(ModelViewSet): serializer_class = BookSerializer queryset = BookInfo.objects.all() # 认证 authentication_classes = [BaseAuthentication, SessionAuthentication] # 权限 permission_classes = [IsAuthenticated] # 限流 throttle_classes = [UserRateThrottle] # 过滤 filter_fields = ["btitle", "bread"] # 127.0.0.1:8000/books/?btitle=西游记
对于列表数据,REST framework提供了OrderingFilter过滤器快速指明数据按照指定字段进行排序。
from rest_framework.viewsets import ModelViewSet from book.models import BookInfo from books.serializers import BookSerializer from rest_framework.authentication import SessionAuthentication, BaseAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.throttling import UserRateThrottle from rest_framework.filters import OrderingFilter class BooksModelViewSet(ModelViewSet): serializer_class = BookSerializer queryset = BookInfo.objects.all() # 认证 authentication_classes = [BaseAuthentication, SessionAuthentication] # 权限 permission_classes = [IsAuthenticated] # 限流 throttle_classes = [UserRateThrottle] # 过滤 # filter_fields = ["btitle", "bread"] # 127.0.0.1:8000/books/?btitle=西游记 # 排序 filter_backends = [OrderingFilter] ordering_fields = ["id", "bread"] # 127.0.0.1:8000/books/?ordering=-bread
6.1 全局:在配置文件中设置全局的分页方式
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100 # 每页数目
}
6.2局部:通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_clas属性来指明。
from rest_framework.viewsets import ModelViewSet from book.models import BookInfo from books.serializers import BookSerializer from rest_framework.authentication import SessionAuthentication, BaseAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.throttling import UserRateThrottle from rest_framework.filters import OrderingFilter from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination # 自定义Pagination类 class PageNum(PageNumberPagination): page_size_query_param = 'page_size' # 指定控制页面展示数量的参数 max_page_size = 10 # 最大展示数量 class BooksModelViewSet(ModelViewSet): serializer_class = BookSerializer queryset = BookInfo.objects.all() authentication_classes = [BaseAuthentication, SessionAuthentication] permission_classes = [IsAuthenticated] throttle_classes = [UserRateThrottle] # filter_fields = ["btitle", "bread"] # 127.0.0.1:8000/books/?btitle=西游记 filter_backends = [OrderingFilter] ordering_fields = ["id", "bread"] # 127.0.0.1:8000/books/?ordering=-bread # 分页 pagination_class = PageNum
6.3 可选分页器
127.0.0.1/books/?page=1&page_size=2
127.0.0.1:8000/books/?offset=3&limit=2
REST framework提供了异常处理,我们可以自定义异常处理函数。
在配置文件中声明自定义的异常处理
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
示例:处理关于数据库的异常
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework import status
from django.db import DatabaseError
def exception_handler(exc, context):
response = drf_exception_handler(exc, context)
if response is None:
view = context['view']
if isinstance(exc, DatabaseError):
print('[%s]: %s' % (view, exc))
response = Response({'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
return response
REST framework定义的异常:
REST framework可以自动帮助我们生成接口文档。接口文档以网页的方式呈现。自动接口文档能生成的是继承自APIView及其子类的视图。
pip install coreapi
from rest_framework.documentation import include_docs_urls
urlpatterns = [
...
url(r'^docs/', include_docs_urls(title='My API title'))
]
1)单一方法的视图,可直接使用类视图的文档字符串
class BookListView(generics.ListAPIView):
"""
返回所有图书信息.
"""
2) 包含多个方法的视图,在类视图的文档字符串中,分开方法定义
class BookListCreateView(generics.ListCreateAPIView):
"""
get:
返回所有图书信息.
post:
新建图书.
"""
3) 对于视图集ViewSet,仍在类视图的文档字符串中分开定义,但是应使用action名称区分
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
"""
list:
返回图书列表数据
retrieve:
返回图书详情数据
latest:
返回最新的图书数据
read:
修改图书的阅读量
"""
class BookInfo(models.Model):
...
bread = models.IntegerField(default=0, verbose_name='阅读量', help_text='阅读量')
...
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。