Django REST Framework 简单入门
* 为什么要用Django REST Framework?
- 前后端分离的业务需要搭建API
- 基于Django可快速开发RESTful API
* Django REST framework如何使用?
快速开始
- 序列化
- 请求和响应
- 基于类的视图
- 认证和权限
- 关联和超链接的APIs
- 视图集和路由
- 概要和客户端库
* RESTful API规范是什么?
- GET(SELECT):从服务器取出资源(一项或多项)
- POST(CREATE):在服务器新建一个资源
- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)
- PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)
- DELETE(DELETE):从放一起删除资源
- HEAD:获取资源的元数据
- OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的
简单入门
*、使用django rest framework之前,需先在项目setting.py中添加必要配置:
1)将 rest_framework 注册到 INSTALLED_APPS 中:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'rest_framework', #新增,drf
]
View Code
2)全局配置 REST_FRAMEWORK :
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAdminUser',
],
'PAGE_SIZE': 1
}
View Code
注:需安装djangorestframework :pip install djangorestframework
一、序列化
1、Django自带序列化方法:
注意:
1. Python内置 json 方法只能序列化python自带基本数据类型,如字符串、列表。字典等,不能直接序列化querysets类型、datetime类型、image类型等(可以借用django内置的model_to_dict 方法完成querysets序列化,不能序列化datetime类型)
2) 可以通过django自带的serializers 方法,可以将querysets序列化成json格式
在前后端分离得项目中,不建议使用django自带serializers方法对数据进行序列化,建议使用 django-rest-framework 自带的 serializer 方法(功能齐全)。
from django.http import HttpResponse
from django.shortcuts import render
from .models import Publisher # models中创建了Publisher 表,有name、address字段
def publisher_list(request):
queryset = Publisher.objects.all() # querysets集合
# 方法1
data = []
for i in queryset:
temp = {
"name": i.name,
"address": i.address
}
data.append(temp)
# 方法2
data = []
from django forms.models import model_to_dict # model转字典
for i in queryset:
data.append(model_to_dict(i))
import json
return HttpResponse(json.dumps(data),content_type="application/json") # 方法1、2返回需json.dumps处理
# 方式三 ,使用django自带序列化serializers
from django.core import serializers
data = serializers.serialize("json",queryset) # 直接序列化成json形式
return HttpResponse(data,content_type="application/json")
2、使用drf自带Serializer、ModelSerializer (drf下的serializers类似于django下的Form组件、ModelSerializer类似于ModelForm)
2.1、使用serializers.Serializer:
1)app01下新建serializers.py文件
2)编写serializers.py:
from rest_framework import serializers
from app01 import models
class PublisherSerializer(serializers.Serializer): #serializers.Serializer
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(max_length=32)
address = serializers.CharField(max_length=128)
# code = serializers.CharField(style={'base_template': 'textarea.html'})
def create(self, validated_data): # 数据验证通过,数据都在validated_data里面,调用serializer.save()时触发
"""创建"""
return models.Publisher.objects.create(**validated_data)
def update(self, instance, validated_data): # 数据验证通过,数据都在validated_data里面,调用serializer.save()时触发
"""更新"""
instance.name = validated_data.get("name", instance.name) #获取validated_data中的数据,如没有则使用原来数据(instance.name)
instance.address = validated_data.get("address", instance.address)
instance.save()
return instance
serializers.Serializer 与Django.Form相似:
- 字段中包含验证标志,如:required、max_length、default等
- 渲染HTML上,上面的style={'base_template': 'textarea.html'} ,类似于django form中的 widget=widgets.Textarea
3)使用serializers.Serializer 时,碰到某个字段展示(序列化)跟校验(反序列化,即前端提交数据,进行反序列化验证各字段)不一致时,可采用 read_only=True 、 write_only=True 两种方式来处理。
如model中某表有个性别字段(sex), sex=((1, "男"),(2, "女")) ,我们希望序列化展示给前端时显示的是"男"或"女", 而前端提交数据时往往获取到的是对应的 1 或 2 ,这种情况就可以采用如下方式处理:
class BookSerializer(serializers.Serializer):
sex = serializers.CharField(source="get_category_display", read_only=True) # 以只读方式展示给前端,前端返回数据时忽略该字段。 获取到的str类型的数据
post_sex = serializers.IntegerField(write_only=True) # 只写方式,只对前端返回的数据进行处理,展示时忽略该字段。 获取到的是int类型的数据
4)serializers.Serializer 中对字段及所有字段验证的方式与Django中的Form验证方式类似。如下:
class BookSerializer(serializers.Serializer):
category = serializers.CharField(source="get_category_display", read_only=True)
post_category = serializers.IntegerField(write_only=True)
publisher_id = serializers.IntegerField(write_only=True)
author_list = serializers.ListField(write_only=True) # ListField:列表类型的字段
def create(self, validated_data):
# 需重写create 方法
book_obj = Book.objects.create(title=validated_data["title"], pub_time=validated_data["pub_time"], category=validated_data["post_category"], publisher_id=validated_data["publisher_id"])
print(book_obj)
book_obj.authors.add(*validated_data["author_list"])
return book_obj
def update(self, instance, validated_data):
# instance 更新的book_obj 对象。 需重写update 方法
instance.title = validated_data.get("title", instance.title)
instance.pub_time = validated_data.get("pub_time", instance.pub_time)
instance.category = validated_data.get("post_category", instance.category)
instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
if validated_data.get("author_list"):
instance.authors.set(validated_data["author_list"])
instance.save()
return instance
def validate_title(self, value):
# 单字段验证
if "python" not in value.lower():
raise serializers.ValidationError("标题必须含有python")
return value
def validate(self, attrs):
# 对所有字段进行验证。 attrs 字典有你传过来的所有的字段
print(attrs)
if "python" in attrs["title"].lower() or attrs["post_category"] == 1:
return attrs
else:
raise serializers.ValidationError("分类或标题不合符要求")
也可自定义验证方法,再添加到指定字段中:
def my_validate(value):
if "敏感信息" in value.lower():
raise serializers.ValidationError("有敏感词汇")
return value
class PublisherSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=32, validators=[my_validate,])
2.2、使用serializers.ModelSerializer:
编写serializers.py:
- 省去了字段的手动添加
- 默认简单实现了creat()、update()方法
注意:Serializers 、 ModelSerializer中实现返回的都是字典格式,不是序列化的字符串,如需返回给前端json格式,则需要进一步json.dumps()
1)ModelSerializer 的基本使用:
class PublisherSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publisher # 于models.py中的Publisher 类对应
fields = "__all__"
# fields = ["id", "title", "pub_time"]
depth = 1 # 嵌套层数 ,会让所有的外键关系字段变成read_only=True 只读类型
read_only_fields = ["id"] # 只读字段
# exclude=["id"]
extra_kwargs = {"publisher": {"write_only": True}, "authors":{"write_only": True}, "title": {"validators": [my_validate,]}} # 用于配置各字段参数,包括类似Serializer自定义字段验证
2)外键关联的对象有很多字段是用不到的,都传给前端会会显得数据冗余。可以自己定制序列化外键对象需要的字段。使用 SerializerMethodField 方法:
class BookSerializer(serializers.ModelSerializer):
category = serializers.CharField(source="get_category_display") # 显示该字段对应中文
# SerializerMethodField的使用,获取显示外联字段
publisher_info = serializers.SerializerMethodField(read_only=True) # 需与get_publisher_info方法配合使用 ,用于前端显示publisher字段数据(只读)
authors_info = serializers.SerializerMethodField(read_only=True) # 需与get_authors_info方法配合使用, 用于前端显示authors字段数据(只读)
def get_authors_info(self, obj):
authors_query_set = obj.author.all() # 拿到所有作者信息
return [{"id": authors_obj.id, "name": authors_obj.name} for authors_obj in authors_query_set] # authors_info字段显示到前端的内容
def get_publisher_info(self, obj):
# obj是我们序列化的每个Book对象
publisher_obj = obj.publisher # 正向查询
return {'id': publisher_obj.id} # publisher_info字段显示的内容
class Meta:
model = Book # 与Book表对应
fields = "__all__"
extra_kwargs = {"publisher": {"write_only": True}, "authors":{"write_only": True}} # publisher、authors 采用只写方式
# 使用上述方式,数据序列化后显示到前端时,publisher、authors字段显示的是我们设定的publisher_info、authors_info 字段的值;而前端传回数据时,对应的publisher、authors字段就用原来的publisher、authors字段字段来进行验证处理。
# 这种方式与Serializer处理只读、只写字段是一致的,只是逻辑有点复杂
3)ModelSerializer 的单字段及全字段验证
与Serializer 中的单字段、全字段验证一致,不累述
4)Serializer 、 ModelSerializer 验证优先级:
自定义验证 > 单字段验证 > 全字段验证
3、在app01的views.py上使用:
from django.shortcuts import render,HttpResponse
from .models import Publisher
from .serializers import PublisherSerializer
import json
def publisher_list(request):
queryset = Publisher.objects.all() # querysets集合
serializer = serializers.PublisherSerializer(queryset,many=True) # queryset是实例集合,需要加 many=True ,如果是单个实例,可以不用加 many=True
#此时,serializer是个字典格式:print(serializer.data)
# {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
# 要序列化,则需要json处理
return HttpResponse(json.dumps(serializer.data),content_type="application/json")
# 或者直接用JSONResponse
# return JSONResponse(serializer.data)
注意:序列化后取数据↓
serializer.data:序列化后数据
serializer.validated_data:前端返回数据经过is_valid()验证,通过后数据存在这里
打印下 serializer.data、json.dumps(serializer.data)、validated_data 看看:
# serializer.data:
{'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
# json.dumps(serializer.data):
'{"id": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'
# validated_data:
OrderedDict([('title', ''), ('code', 'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
4)最后一步,在url.py中做配置:
from django.urls import path
from app01 import views
urlpatterns = [
path('publish/', views.publisher_list),
]
此时,序列化相关的简单测试便完成了,编写完成后可进行测试看效果。
关于get、post、put、delete示例:
1)列出所有的code snippet,或创建一个新的snippet
@csrf_exempt
def snippet_list(request):
"""
列出所有的code snippet,或创建一个新的snippet。
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return JSONResponse(serializer.data)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = SnippetSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data, status=201) # 状态码201 ,创建成功
return JSONResponse(serializer.errors, status=400) # 状态码400 ,用户请求错误
示例1
2)获取,更新或删除一个 code snippet
@csrf_exempt
def snippet_detail(request, pk):
"""
获取,更新或删除一个 code snippet。
"""
try:
snippet = Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
return HttpResponse(status=404) # 404, not found
if request.method == 'GET':
serializer = SnippetSerializer(snippet)
return JSONResponse(serializer.data)
elif request.method == 'PUT':
data = JSONParser().parse(request)
serializer = SnippetSerializer(snippet, data=data)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data)
return JSONResponse(serializer.errors, status=400)
elif request.method == 'DELETE':
snippet.delete()
return HttpResponse(status=204) # 204,删除数据成功
View Code
二、请求和响应
1、请求对象
REST框架引入了一个扩展了常规 HttpRequest
的 Request
对象,并提供了更灵活的请求解析。Request
对象的核心功能是 request.data
属性,它与 request.POST
类似,但对于使用Web API更为有用。
request.POST :只处理表单数据 只适用于'POST'方法
request.data :处理任意数据 适用于'POST','PUT'和'PATCH'方法
2、响应对象
REST框架还引入了一个 Response
对象,这是一种获取未渲染(unrendered)内容的 TemplateResponse
类型,并使用内容协商来确定返回给客户端的正确内容类型。
return Response(data) # 渲染成客户端请求的内容类型。
3、状态码
在你的视图(views)中使用纯数字的HTTP 状态码并不总是那么容易被理解。而且如果错误代码出错,很容易被忽略。REST框架为status
模块中的每个状态代码(如HTTP_400_BAD_REQUEST
)提供更明确的标识符。使用它们来代替纯数字的HTTP状态码是个很好的主意。
4、包装API视图
REST框架提供了两个可用于编写API视图的包装器(wrappers)。
- 用于基于函数视图的
@api_view
装饰器。 - 用于基于类视图的
APIView
类。
这些包装器提供了一些功能,例如确保你在视图中接收到Request
实例,并将上下文添加到Response
,以便可以执行内容协商。
包装器还提供了诸如在适当时候返回405 Method Not Allowed
响应,并处理在使用格式错误的输入来访问request.data
时发生的任何ParseError
异常。
使用:
1、列出所有的实例数据、或post一个新的实例:
from django.views.decorators.csrf import csrf_exempt
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Snippet
from .serializers import SnippetSerializer
@csrf_exempt
@api_view(['GET', 'POST']) # 只允许这两种方法
def snippet_list(request):
"""
列出所有的snippets,或者创建一个新的snippet。
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
View Code
输入url网址访问:
http://127.0.0.1:8000/snippet/
2、获取、更新或删除单个实例:
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Snippet
from .serializers import SnippetSerializer
@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
"""
获取,更新或删除一个snippet实例。
"""
try:
snippet = Snippet.objects.get(pk=pk)
print("123",snippet)
except Snippet.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
url.py配置:
urlpatterns = [
re_path(r'^snippet/(\d+)/$', views.snippet_detail),
]
访问单个实例数据:
http://127.0.0.1:8000/snippet/1/ (相比上一个,snippet后面追加了/1/)
上述主要以基于函数视图的@api_view
装饰器示例,下面开始讲基于类视图的
三、基于类的视图
1、基于APIView视图:
1.1、列出所有的实例数据、或post一个新的实例:
from rest_framework import status
from rest_framework.response import Response
from .models import Snippet
from .serializers import SnippetSerializer
# from django.http import Http404
from rest_framework.views import APIView
class SnippetList(APIView):
"""
列出所有的snippets或者创建一个新的snippet。
"""
def get(self, request, format=None):
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
url.py配置:
urlpatterns = [
path('snippet/', views.SnippetList.as_view()),
]
1.2、获取、更新或删除单个实例:
from rest_framework import status
from rest_framework.response import Response
from .models import Snippet
from .serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
class SnippetDetail(APIView):
"""
检索,更新或删除一个snippet示例。
"""
def get_object(self, pk):
try:
return Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
raise Http404
def get(self, request, pk, format=None): #获取单个数据
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
def put(self, request, pk, format=None): # 更新单个数据
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None): # 删除单个数据
snippet = self.get_object(pk)
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
url.py配置:
urlpatterns = [
re_path(r'^snippet/(\d+)/$', views.SnippetDetail.as_view()),
]
页面显示效果与 基于函数视图的一样
2、代码精简,混合使用mixins
2.1、mixins.py:共有五个类
ListModelMixin:get方法,用于列出所有数据
-
CreateModelMixin:post方法,用于创建一个新的数据
-
RetrieveModelMixin:get方法,用于获取单个数据
-
UpdateModelMixin:put方法,用于更新单个数据
-
DestroyModelMixin:delete方法,用于删除数据
2.2、generic.py:
- GenericAPIView:继承于views.APIView , GenericAPIView
- ListAPIView:继承于 ListModelMixin , GenericAPIView
- CreateAPIView:继承于 CreateModelMixin , GenericAPIView
- RetrieveAPIView:继承于 RetrieveModelMixin , GenericAPIView
- DestroyAPIView:继承于 DestroyModelMixin , GenericAPIView
- UpdateAPIView:继承于 UpdateModelMixin , GenericAPIView
- ListCreateAPIView:继承于 ListModelMixin、CreateModelMixin , GenericAPIView
- RetrieveUpdateAPIView:继承于 RetrieveModelMixin、UpdateModelMixin , GenericAPIView
- RetrieveDestroyAPIView:继承于 RetrieveModelMixin、DestroyModelMixin , GenericAPIView
- RetrieveUpdateDestroyAPIView:继承于 RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin , GenericAPIView
注意:generic.GenericAPIView很重要,GenericAPIView中实现了很多在很多场景都需要的方法,如序列化、搜索、过滤、分页操作等等。
简单讲解:
mixins.py中的五个类,分别实现五种请求方法,在项目中使用时重写相应的请求方法即可
generic.py:GenericAPIView封装了很多方法,包括获取数据、序列化等等,mixins.py中的各类分别封装的方法会直接调用py:GenericAPIView中的方法,比如py:GenericAPIView中已经实现的序列化操作,ListModelMixin通过
self.get_serializer()方法即可获得序列化数据
示例 1:使用mixins中的类 与 generic中的类结合:mixins中的类实现get、post、put、delete等方法, generic中的类实现获取数据给mixins、数据序列等
1)列出所有的实例数据、或post一个新的实例:
直接继承 ListModelMixin等方法,可以避免代码冗余重复
from .models import Snippet
from .serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics
class SnippetList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all() # GenericAPIView中调用
serializer_class = SnippetSerializer # GenericAPIView中调用
def get(self, request, *args, **kwargs): # 重写 ListModelMixin的方法
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs): # 重写 CreateModelMixin的方法
return self.create(request, *args, **kwargs)
2)获取、更新或删除单个实例:
from .models import Snippet
from .serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics
class SnippetDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all() # GenericAPIView 调用
serializer_class = SnippetSerializer # GenericAPIView 调用
def get(self, request, *args, **kwargs): # RetrieveModelMixin 调用
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs): # UpdateModelMixin 调用
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs): # DestroyModelMixin 调用
return self.destroy(request, *args, **kwargs)
示例2:直接使用generic.py中的类,因为它们其实也继承了mixins中各类的特点,
如下代码,简简单单几行代码,便能进一步实现了所需要的功能
from .models import Snippet
from .serializers import SnippetSerializer
from rest_framework import generics
# 列出所有的实例数据、或post一个新的实例
class SnippetList(generics.ListCreateAPIView):
# 继承了 mixins.ListModelMixin、mixins.CreateModelMixin , generics.GenericAPIView
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
# 获取、更新或删除单个实例
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
# 继承了 mixins.RetrieveModelMixin、mixins.UpdateModelMixin、mixins.DestroyModelMixin , generics.GenericAPIView
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
url.py配置是一样的
urlpatterns = [
path('snippet/', views.SnippetList.as_view()),
re_path(r'^snippet/(\d+)/$', views.SnippetDetail.as_view()),
]
四、认证和权限
1、学习认证前,先配置url.py,开放登录调试口:
urlpatterns = [
# 调试登录,可用admin账号登录drf页面
path('api-auth/', include('rest_framework.urls',namespace='rest_framework')),
]
2、基本的认证:登录才能访问到数据
默认未登录不可访问数据
课前准备:
1)models.py中的 Snippet 新增user字段,继承于django自带的auth.User:
operator = models.ForeignKey("auth.User")
执行makemigrations 、 migrate操作
2)在serializers.py中的SnippetSerializer 新增字段:
operator = serializers.ReadOnlyField(source="operator.username")
'
'
ModelSerializer
2.2、设置所有人可以访问数据:
from .models import Snippet
from .serializers import SnippetSerializer
from rest_framework import generics
# 列出所有的实例数据、或post一个新的实例
class SnippetList(generics.ListCreateAPIView):
# 继承了 mixins.ListModelMixin、mixins.CreateModelMixin , generics.GenericAPIView
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = () # 用于设置权限,目前是无权限限制,谁都可以访问数据
# 获取、更新或删除单个实例
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
# 继承了 mixins.RetrieveModelMixin、mixins.UpdateModelMixin、mixins.DestroyModelMixin , generics.GenericAPIView
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = ()
2.3、设置只有登录才能访问数据(默认如此):
from .models import Snippet
from .serializers import SnippetSerializer
from rest_framework import generics
from rest_framework import permissions
# 列出所有的实例数据、或post一个新的实例
class SnippetList(generics.ListCreateAPIView):
# 继承了 mixins.ListModelMixin、mixins.CreateModelMixin , generics.GenericAPIView
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticated,) # 登录后才能访问数据
# 获取、更新或删除单个实例
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
# 继承了 mixins.RetrieveModelMixin、mixins.UpdateModelMixin、mixins.DestroyModelMixin , generics.GenericAPIView
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = () # 无权限限制
View Code
注意:将 permission_classes 中的 permissions.IsAuthenticated 换成 permissions.IsAuthenticatedOrReadOnly
IsAuthenticatedOrReadOnly :只有登录的情况下,可以访问数据,未登录的情况下,只能访问只读数据,即get、HEAD操作等
2.4、自定义认证
IsOwnerOrReadOnly:能访问只读数据,只有是作者本人才有操作的权限
1)在app01中新建permissions.py文件:
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
自定义权限只允许对象的所有者编辑它。
"""
def has_object_permission(self, request, view, obj):
# 读取权限允许任何请求,
# 所以我们总是允许GET,HEAD或OPTIONS请求。
if request.method in permissions.SAFE_METHODS: #只读数据是安全的,可以给访问
return True
# 只有该snippet的所有者才允许写权限。
return obj.operator== request.user
2)view.py中添加:
permission_classes = (permissions.IsAuthenticated,IsOwnerOrReadOnly)
这样就可以了。
注意:之前我们在serializers.py中新增的operator字段,因为不在models.Snippet表中,当前端新建并保存数据、或者更新数据用到时,serializers中会不知道怎么处理operator这个字段,因此需要在views.py中需要用到这个字段的地方,添加
perform_create方法:
class SnippetList(generics.ListCreateAPIView):
# 继承了 mixins.ListModelMixin、mixins.CreateModelMixin , generics.GenericAPIView
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticated,) # 登录后才能访问数据
def perform_create(self, serializer):
serializer.save(operator=self.request.user)
3、还有个token验证(未介绍),一般手机注册、登录验证等,用Json Web Token(JWT)验证比较好,有兴趣查阅:
五、关联和超链接的APIs
1、为我们的API创建一个根路径:
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
@api_view(['GET'])
def api_root(request, format=None):
return Response({
'publishers': reverse('publishers-list', request=request, format=format), # user_list是url中命名的别名
'books': reverse('books-list', request=request, format=format) })
当前端访问url时,没有指定get某个或某些数据,只是访问根url时,将返回api_root函数中编好的路径,客户可根据提示进行访问:
2、超链接我们的API
处理好实体之间的关系是Web API设计中更具挑战性的方面。我们可以选择几种不同的方式来代表一种关系:
- 使用主键。
- 在实体之间使用超链接。
- 在相关实体上使用唯一的标识字段。
- 使用相关实体的默认字符串表示形式。
- 将相关实体嵌套在父表示中。
- 一些其他自定义表示。
REST框架支持所有这些方式,并且可以将它们应用于正向或反向关系,也可以在诸如通用外键之类的自定义管理器上应用。
在这种情况下,我们希望在实体之间使用超链接方式。这样的话,我们需要修改我们的序列化程序来扩展HyperlinkedModelSerializer
而不是现有的ModelSerializer
。
HyperlinkedModelSerializer
与ModelSerializer
有以下区别:
- 默认情况下不包括
id
字段。 - 它包含一个
url
字段,使用HyperlinkedIdentityField
。 - 关联关系使用
HyperlinkedRelatedField
,而不是PrimaryKeyRelatedField
。
我们可以轻松地重写我们现有的序列化程序以使用超链接:
class BookSerializer(serializers.HyperlinkedModelSerializer): #只需要将ModelSerializer 换成 HyperlinkedModelSerializer
class Meta:
model = models.Book
fields = (
"id",
"title",
"publisher"
)
页面效果:
六、视图集和路由器
viewsets.py中,常用view:
- GenericViewSet:继承于 viewsets.ViewSetMixin , generics.GenericAPIView
- ModelViewSet:继承于 mixins中的所有类(五个类) , viewsets.GenericViewSet(即上面一个类)
- ReadOnlyModelViewSet: 继承于 mixins.RetrieveModelMixin、mixins.ListModelMixin , viewsets.GenericViewSet
1、使用viewsets重构
删除原先views,py中 SnippetList类、SnippetDetail类 , 重新创建一个 SnippetViewSet类:
只需这样,就可以完成跟SnippetList、SnippetDetail一样的功能
class SnipetViewSet(viewsets.ModelViewSet):
"""
ModelViewSet视图自动提供`list`,`create`,`retrieve`,`update`和`destroy`操作。
"""
queryset = models.Book.objects.all()
serializer_class = serializers.BookSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
2、使用路由器
在url.py中配置:
# 创建路由器并注册我们的视图。
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet)
# API URL现在由路由器自动确定。
# 另外,我们还要包含可浏览的API的登录URL。
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
使用路由器注册viewsets类似于提供urlpattern。我们包含两个参数 - 视图的URL前缀和视图本身。
我们使用的DefaultRouter
类也会自动为我们创建API根视图,因此我们现在可以从我们的views
模块中删除api_root
方法。
七、概要和客户端库
略
一般使用rest framework自带的自动化文档功能:
为了提供概要或者文档支持REST框架,需要使用Core API,Core API是用于描述API的文档规范。它用于提供可用路径的内部表示形式和API公开的可能的交互。它可以用于服务器端或客户端。
pip install coreapi
然后配置url.py文件:
from rest_framework.documentation import include_docs_urls
urlpatterns = [
path('docs/', include_docs_urls(title='自动化文档')),
]
访问:http://127.0.0.1:8000/docs/ ,页面效果: