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 中文教程:https://q1mi.github.io/Django-REST-framework-documentation/tutorial/quickstart_zh
*、使用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 ]
2)全局配置 REST_FRAMEWORK :
REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAdminUser', ], 'PAGE_SIZE': 1 }
注:需安装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 ,用户请求错误
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,删除数据成功
二、请求和响应
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)
输入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")
因为'
operator'
在用户模型中是一个反向关联关系。在使用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 = () # 无权限限制
注意:将 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)验证比较好,有兴趣查阅:https://www.cnblogs.com/Eric15/articles/9536129.html
五、关联和超链接的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/ ,页面效果: