Django REST Framework 简单入门


* 为什么要用Django REST Framework?

  1.  前后端分离的业务需要搭建API
  2. 基于Django可快速开发RESTful API

* Django REST framework如何使用?

 快速开始

  1. 序列化
  2. 请求和响应
  3. 基于类的视图
  4. 认证和权限
  5. 关联和超链接的APIs
  6. 视图集和路由
  7. 概要和客户端库

* RESTful API规范是什么?

  1. GET(SELECT):从服务器取出资源(一项或多项) 
  2. POST(CREATE):在服务器新建一个资源
  3. PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)
  4. PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)
  5. DELETE(DELETE):从放一起删除资源
  6. HEAD:获取资源的元数据
  7. 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
]
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更为有用。

  1. request.POST :只处理表单数据 只适用于'POST'方法
  2. 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)。

  1. 用于基于函数视图的@api_view装饰器。
  2. 用于基于类视图的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:共有五个类

  1. ListModelMixin:get方法,用于列出所有数据
  2. CreateModelMixin:post方法,用于创建一个新的数据
  3. RetrieveModelMixin:get方法,用于获取单个数据
  4. UpdateModelMixin:put方法,用于更新单个数据
  5. DestroyModelMixin:delete方法,用于删除数据

2.2、generic.py:

  1. GenericAPIView:继承于views.APIView , GenericAPIView
  2. ListAPIView:继承于 ListModelMixin , GenericAPIView
  3. CreateAPIView:继承于 CreateModelMixin , GenericAPIView
  4. RetrieveAPIView:继承于 RetrieveModelMixin , GenericAPIView
  5. DestroyAPIView:继承于 DestroyModelMixin , GenericAPIView
  6. UpdateAPIView:继承于 UpdateModelMixin , GenericAPIView
  7. ListCreateAPIView:继承于 ListModelMixin、CreateModelMixin , GenericAPIView
  8. RetrieveUpdateAPIView:继承于 RetrieveModelMixin、UpdateModelMixin , GenericAPIView
  9. RetrieveDestroyAPIView:继承于 RetrieveModelMixin、DestroyModelMixin , GenericAPIView
  10. 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 = ()  # 无权限限制
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)验证比较好,有兴趣查阅: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

HyperlinkedModelSerializerModelSerializer有以下区别:

  • 默认情况下不包括id字段。
  • 它包含一个url字段,使用HyperlinkedIdentityField
  • 关联关系使用HyperlinkedRelatedField,而不是PrimaryKeyRelatedField

我们可以轻松地重写我们现有的序列化程序以使用超链接:

class BookSerializer(serializers.HyperlinkedModelSerializer): #只需要将ModelSerializer 换成 HyperlinkedModelSerializer

    class Meta:
        model = models.Book
        fields = (
            "id",
            "title",
            "publisher"
        )

页面效果:

 


 

六、视图集和路由器 

 viewsets.py中,常用view:

  1. GenericViewSet:继承于 viewsets.ViewSetMixin , generics.GenericAPIView
  2. ModelViewSet:继承于 mixins中的所有类(五个类) , viewsets.GenericViewSet(即上面一个类)
  3. 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/ ,页面效果:

 

 

posted on 2018-08-28 20:39  Eric_nan  阅读(608)  评论(0编辑  收藏  举报