Django REST framework 是用于构建 Web API(网络应用程序接口) 的强大而灵活的工具包,利用它我们可以自动生成符合 RESTful 规范的 API 、生成 Browserable 的交互页面以及非常细粒度的权限管理等。

选择使用 REST 框架的一些原因:

  • Web浏览API对于开发人员来说是一个巨大的可用性
  • 认证策略包括OAuth1a和OAuth2的包
  • 支持ORM和非ORM数据源的序列化
  • 如果你不需要更强大的功能,就可以使用常规的基于功能的视图
  • 广泛的文档和良好的社区支持

Django Rest framework 大概有如下流程: mydrf25

  • 创建数据模型
  • 依靠 Serialiers 将数据库取出的数据 Parse 为 API 的数据(可用于返回给客户端,也可用于浏览器显示)
  • 权限设置(对象级别、系统级别等权限管理)
  • ViewSet 是一个 views 的集合,根据客户端的请求(GET、POST等),返回 Serialiers 处理的数据
  • ViewSet 可在 Routers 进行注册,注册后会显示在 Api Root 页上
  • 在 URLs 里注册 ViewSet 生成的 view

基础配置

为该项目创建一个单独的虚拟环境用于隔离其它环境。

mkvirtualenv mydrf

pip install django
pip install djangorestframework
pip install pygments    # 用于代码高亮显示
pip install pymysql

创建 Django 项目及 APP

  • 在虚拟环境下创建 Django 项目
(mydrf) allenlideMacBook-Pro:~ allen$  django-admin.py startproject mydrf
  • 创建 APP
python manage.py startapp snippets    # 用于处理数据 
python manage.py startapp api         # 用于处理 API
  • 注册 APP
# settings.py

INSTALLED_APPS = (
    ...
    'rest_framework',
    'snippets',
    'api',
)
  • 配置数据库连接
# settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mydrf',
        'USER': 'root',
        'PASSWORD': 'password',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'OPTIONS': {'charset': 'utf8',}
    }
}


# __init__.py

import pymysql
pymysql.install_as_MySQLdb()
  • 设置 REST_FRAMEWORK
# settings.py

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.AllowAny',
    ),
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 10     # 分页
}

创建数据模型

  • Snippet 数据模型
# snippets/models.py

from django.db import models

from pygments.lexers import get_all_lexers

LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])


class Snippet(models.Model):
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('created',)

    def __str__(self):
        return self.title
  • 注册 Snippet 模型类
# snippets/admin.py

from django.contrib import admin

from .models import Snippet


@admin.register(Snippet)
class SnippetAdmin(admin.ModelAdmin):
    list_display = ('title', 'code', 'language', 'created')
  • 生成数据库
python manage.py makemigrations
python manage.py migrate
  • 创建超级用户
python manage.py createsuperuser

序列化

Serializers 用于将复杂的数据结构,例如 ORM 中的 QuerySet 或者 Model 实例对象转换成 Python 内置的数据类型,从而进一步方便数据和 json,xml 等格式的数据进行交互。
通常情况下,前端调用后台 API,API 需要返回数据给前端,而返回的数据类型一般以 JSON 格式为主。
Serializers 则可以实现如下功能:

  • 序列化 -- 将 Python Model 实例对象转换为 JSON 格式,返回给前端
  • 反序列化 -- 将前端传给后台的 JSON 数据转换为 Python Model 实例对象

使用 Serializer 类

  • 创建序列化类
# api/serializers.py

from rest_framework import serializers

from snippets.models import Snippet, LANGUAGE_CHOICES


class SnippetSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=100)
    code = serializers.CharField(style={'base_template': 'textarea.html'})
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')

    def create(self, validated_data):
        '''
        根据提供的验证过的数据创建并返回一个新的 `Snippet` 实例。
        '''
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        '''
        根据提供的验证过的数据更新和返回一个已经存在的 `Snippet` 实例。
        '''
        instance.title = validated_data.get('title', instance.title)   # instance.title 为默认值
        instance.code = validated_data.get('code', instance.code)
        instance.language = validated_data.get('language', instance.language)
        instance.save()
        return instance
  • 序列化器类的第一部分,告诉REST框架,哪些字段需要进行序列化/反序列化
  • create() 和 update() 方法定义了在调用 serializer.save() 时如何创建和修改完整的实例
  • 序列化器类与 Django Form 类非常相似,并在各种字段中包含类似的验证标志,例如 required,max_length 和 default
  • {'base_template': 'textarea.html'} 标识,相当于在 Django 的 Form 类中使用 widget=widgets.Textarea,用于 HTML 渲染。
  • 使用序列化程序
# python manage.py shell

>>> from snippets.models import Snippet
>>> from api.serializers import SnippetSerializer
>>> from rest_framework.renderers import JSONRenderer  # 输出格式为json格式
>>> from rest_framework.parsers import JSONParser  # 输入格式为json格式

实例化类

>>> snippet = Snippet(code='foo = "bar"\n')
>>> snippet.save()
>>> snippet = Snippet(code='print "hello, world"\n')
>>> snippet.save()

获取 python 数据类型的数据

>>> serializer = SnippetSerializer(snippet)
>>> serializer.data
{'id': 2, 'title': '', 'code': 'print "hello, world"\n', 'language': 'python'}

此时,我们将模型实例转换为 Python 原生数据类型,要完成序列化过程,我们将数据转换成 json

>>> content = JSONRenderer().render(serializer.data)
>>> content
b'{"id":2,"title":"","code":"print \\"hello, world\\"\\n","language":"python"}'

反序列化是类似的,首先我们将一个流(stream)解析为 Python 原生数据类型

>>> from django.utils.six import BytesIO
>>> stream = BytesIO(content)           # 读取
>>> data = JSONParser().parse(stream)   # 解析
>>> data
{'id': 2, 'title': '', 'code': 'print "hello, world"\n', 'language': 'python'}

然后我们要将 Python 原生数据类型恢复成正常的对象实例

>>> serializer = SnippetSerializer(data=data)   # 反向还原(反序列化)
>>> serializer.is_valid()       # 验证还原数据
True
>>> serializer.validated_data   # 查看还原数据
OrderedDict([('title', ''),('code', 'print "hello, world"'),('language', 'python')])
>>> serializer.save()           # 保存
<Snippet: >

检查数据 mydrf01

对 JSON 格式字典进行校验并写入数据模型中

>>> snippet = {'code':'Django REST Framework.'}
>>> s = SnippetSerializer(data=snippet)
>>> s.is_valid()
True
>>> s.save()
<Snippet: >

检查数据> mydrf02

使用 many=True 标志,序列化查询结果集而不是模型实例

>>> serializer = SnippetSerializer(Snippet.objects.all(), many=True)
>>> serializer.data
[OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar"\n'), ('language', 'python')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print "hello, world"\n'), ('language', 'python')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print "hello, world"'), ('language', 'python')]), OrderedDict([('id', 4), ('title', ''), ('code', 'Django REST Framework.'), ('language', 'python')])]
  • 在视图中使用 Serializer 类

创建一个 HttpResponse 的子类,这个子类会将任何 data 渲染并返回为 json

# api/views.py

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser

from snippets.models import Snippet
from api.serializers import SnippetSerializer


class JSONResponse(HttpResponse):
    '''
    An HttpResponse that renders its content into JSON.
    '''
    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data)
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)

因为我们需要 POST 数据到这个视图的客户端,但并没有 CSRF 令牌(token),所以我们需要将该视图标记为 csrf_exempt

# api/views.py

@csrf_exempt
def snippet_list(request):
    '''
    列出所有的 `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)
        return JSONResponse(serializer.errors, status=400)


@csrf_exempt
def snippet_detail(request, pk):
    '''
    获取、更新或删除一个 `Snippet`
    '''
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)

    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)        
  • 配置 URLs 访问数据
# urls.py

from django.conf.urls import url, include

urlpatterns = [
    ...,
    url(r'^api/', include('api.urls')),
]


# api/urls.py  路由分发

from django.conf.urls import url
from . import views

app_name='api'
urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]
  • 测试 API

启动服务

python manage.py runserver

访问所有的 Snippet 列表 mydrf03

通过引用其 id 来访问特定的 Snippet mydrf04

使用 ModelSerializer 类(**)

就像 Django 提供了 Form 类和 ModelForm 类一样,REST framework 也包括 Serializer 类和 ModelSerializer 类
仅修改 serializers.py 文件, views 和 urls 文件不用更改。

# api/serializers.py

from rest_framework import serializers

from snippets.models import Snippet


class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'language')

ModelSerializer 类并不会做任何特别神奇的事情,它们只是创建序列化器类的快捷方式:

  • 一组自动确定的字段或全部字段
  • 默认简单实现的 create() 和 update() 方法

使用 ModelSerializer 类

# python manage.py shell

>>> from snippets.models import Snippet
>>> from api.serializers import SnippetSerializer

>>> snippet = {'code':'Django REST Framework By ModelSerializer.'}
>>> s = SnippetSerializer(data=snippet)
>>> s.is_valid()
True
>>> s.save()
<Snippet: >

mydrf05

请求和响应

请求对象

REST framework 引入了一个 Request 对象, 它继承自普通的 HttpRequest ,但能够更加灵活的解析收到的请求。Request 对象的核心功能,就是其中的 request.data 属性。这个属性跟 request.POST 相似,但对我们的 Web API来说,更加的有用。

request.POST  # 只能处理表单数据,只能处理 'POST' 方法.
request.data  # 可处理任意数据.可以处理 'POST', 'PUT' 和 'PATCH'方法

响应对象

REST 框架还引入了一个 Response 对象,是一种 TemplateResponse ,它携带着纯粹的内容(未经过渲染),通过内容协商(Content Negotiation)来决定将以何种形式,返回给客户端。

return Response(data)  # 根据客户端的要求,渲染成不同的内容类型

状态码

在视图中,使用纯数字的状态码,并不利于代码阅读,如果你写错了状态码,也不会很容易的察觉。REST framework 为每个状态码提供了更加明确的标识,比如 HTTP_400_BAD_REQUEST,这些标识符在 status 模块中。

from rest_framework import status

包装API视图

REST 框架提供了两个可用于编写API视图的包装器:

  • 用于处理基于函数视图的装饰器 @api_view
  • 用于处理基于类视图的 APIView 类

这些包装器提供了一些功能,例如确保你在视图能够接收到 Request 实例,并将上下文添加到 Response 对象,以便可以执行内容协商;
这些包装器还内置了一些行为,例如在遇到错误请求时,自动响应 405 Method Not Allowed; 在处理 request.data 时,因为输入的格式不恰当,而发生的任何 ParseError 异常。

将所有元素组合在一起

  • 使用包装器重新编写视图
# api/views.py

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from snippets.models import Snippet
from api.serializers import SnippetSerializer


@api_view(['GET', 'POST'])
def snippet_list(request):
    '''
    列出所有的 `Snippet`,或者创建一个新的 `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)


@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
    '''
    获取,更新或删除一个 `Snippet` 实例
    '''
    try:
        snippet = Snippet.objects.get(pk=pk)
    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)
  • 为 URLs 添加可选的格式后缀 为了充分利用我们的响应不再与单一内容类型连接,我们可以为 API 路径添加对格式后缀的支持。使用格式后缀给我们明确指定了给定格式的 URLs,这意味着我们的 API 将能够处理诸如 http://example.com/api/items/4.json 之类的 URL

在视图中添加一个 format 关键字参数

# api/views.py

def snippet_list(request, format=None):


def snippet_detail(request, pk, format=None):

配置 URLs

# api/urls.py

from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns

from . import views

app_name='api'
urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]

urlpatterns = format_suffix_patterns(urlpatterns)

访问所有的 Snippet 列表 mydrf06

通过引用其 id 来访问特定的 Snippet mydrf07

使用 httpie 测试服务器

pip install httpie

通过附加格式后缀,控制回复的响应格式

http http://127.0.0.1:8000/api/snippets.json

HTTP/1.1 200 OK
Content-Type: application/json
[
    {
        "code": "foo = \"bar\"\n",
        "id": 1,
        "language": "python",
        "title": ""
    },
    {
        "code": "print \"hello, world\"\n",
        "id": 2,
        "language": "python",
        "title": ""
    }
]

POST 表单数据,新增 Snippet

http --form POST http://127.0.0.1:8000/api/snippets/ code="print 123"

HTTP/1.1 201 Created
{
    "code": "print 123",
    "id": 13,
    "language": "python",
    "title": ""
}

POST JSON 数据,新增 Snippet

http --json POST http://127.0.0.1:8000/api/snippets/ code="print 456"

HTTP/1.1 201 Created
{
    "code": "print 456",
    "id": 14,
    "language": "python",
    "title": ""
}

PUT 表单数据,修改 Snippet

http --form PUT http://127.0.0.1:8000/api/snippets/13/ code="print 789"

HTTP/1.1 200 OK
{
    "code": "print 789",
    "id": 13,
    "language": "python",
    "title": ""
}

DELETE JSON 数据, 删除 Snippet

http --json DELETE http://127.0.0.1:8000/api/snippets/14/

HTTP/1.1 204 No Content

视图

基于类的视图

  • 编写视图
# api/views.py

from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from snippets.models import Snippet
from api.serializers import SnippetSerializer


class SnippetList(APIView):
    '''
    列出所有的 `Snippet` 或者创建一个新的 `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)


class SnippetDetail(APIView):
    '''
    检索,更新或删除一个 `Snippet` 示例
    '''
    def get_object(self, pk):
    # 重写 get_obj 方法,根据 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)
  • 配置 URLs
# api/urls.py

from django.conf.urls import url
from . import views

app_name='api'
urlpatterns = [
    url(r'^snippets/$', views.SnippetList.as_view()),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()),
]

mydrf08

通用的基于类的视图

  • 编写视图
# api/views.py

from rest_framework import generics

from snippets.models import Snippet
from api.serializers import SnippetSerializer


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

mydrf09 mydrf10

  • 配置 URLs
# api/urls.py

from django.conf.urls import url
from . import views

app_name='api'
urlpatterns = [
    url(r'^snippets/$', views.SnippetList.as_view()),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()),
]

认证和权限(**)

目前,我们的API对谁可以编辑或删除代码段没有任何限制。我们希望有更高级的行为,以确保:

  • 代码片段始终与创建者相关联
  • 只有通过身份验证的用户可以创建片段
  • 只有代码片段的创建者可以更新或删除它
  • 未经身份验证的请求应具有完全只读访问权限

关联 User 与 Snippet 模型

  • Snippet 模型增加用户字段
# snippets/models.py

from django.contrib.auth.models import User

class Snippet(models.Model):
    owner = models.ForeignKey(User, related_name='snippets', on_delete=models.CASCADE)
    ...,
  • 生成数据库 因为存在外键关联关系,所以在生成数据库的时候,需要指定默认值
python manage.py makemigrations snippets
python manage.py migrate snippets
  • 序列化类中新增该字段
# api/serializers.py

class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'language', 'owner')
  • 访问 URLs

mydrf11

  • 修改显示内容(更新序列化类) 由上图可知,owner 字段显示的是用户 id,如果需要显示用户 name,还需要进行如下修改
# api/serializers.py

class SnippetSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')   # 关联用户

    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'language', 'owner')
  • source 参数控制哪个属性用于填充字段,并且可以指向序列化实例上的任何属性
  • 无类型的 ReadOnlyField 类始终是只读的,只能用于序列化表示,不能用于在反序列化时更新模型实例
  • 访问 URLs

mydrf12

  • 新增一个用户
python manage.py createsuperuser
  • 回收该用户的 Superuser 权限(用于区分 admin 用户) mydrf13

自定义权限(录入用户权限)

设置只有数据录入用户才有权限进行编辑或修改,其它用户只有只读权限

  • 使用 has_object_permission(对象权限), 需传递数据对象
# api/permissions.py

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    '''
    自定义权限-只允许对象的所有者进行编辑/删除
    此处 obj 表示 Snippet 对象        
    '''
    def has_object_permission(self, request, view, obj):
        # 读取的权限允许任何请求
        # 所以我们总是允许 GET 、HEAD 、OPTIONS 请求
        if request.method in permissions.SAFE_METHODS:
            return True

        # 只有对象的所有者才允许写权限
        # 返回 True/False  True 代表有权限,False 代表无权限
        return obj.owner == request.user
  • 在 API 界面新增用户登录/注销接口
# urls.py

urlpatterns = [
    ...,
    url(r'^api-auth/', include(('rest_framework.urls', 'api-auth'), namespace='rest_framework'))
]
  • 新增 Snippet 模型对象(录入人员为 admin2)
# python manage.py shell

>>> from snippets.models import Snippet
>>> from django.contrib.auth.models import User

>>> u = User.objects.get(pk=2)
>>> s = Snippet(code='@login_required', owner=u)
>>> s.save()
  • 修改视图,加入自定义权限认证
# api/views.py

from rest_framework import generics

from .serializers import SnippetSerializer
from .permissions import IsOwnerOrReadOnly
from snippets.models import Snippet


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (IsOwnerOrReadOnly, )   # 注意这里是元祖

    def perform_create(self, serializer):
        '''
        重写 perform_create 方法,在保存模型实例时,会自动将录入用户写入模型实例
        '''
        serializer.save(owner=self.request.user)    


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (IsOwnerOrReadOnly, )

重写 perform_create 方法的作用: 将当前登录用户写入数据模型实例中 mydrf14
mydrf15

  • 验证 admin 用户是否有权限编辑/删除(非录入人员) 非录入人员无 "PUT" 窗口 mydrf16

  • 验证 admin2 用户是有否权限编辑/删除(录入人员) mydrf17

自定义权限(管理员权限)

设置只有管理员(Superuser)用户才有权限进行编辑或修改,其它用户只有只读权限(录入人员也无法修改)

  • 使用 has_permission(系统级权限)
# permissions.py

from rest_framework import permissions

class IsAdminUserOrReadOnly(permissions.BasePermission):
    def has_permission(self, request, view):
        '''
        系统级权限,不接受 obj 对象
        '''
        if request.method in permissions.SAFE_METHODS:
            return True
        return request.user and request.user.is_superuser
  • 修改视图,加入自定义权限认证
# api/views.py

from rest_framework import generics

from .serializers import SnippetSerializer
from .permissions import IsAdminUserOrReadOnly
from snippets.models import Snippet


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (IsAdminUserOrReadOnly, )

    def perform_create(self, serializer):
        '''
        重写 perform_create 方法,在保存模型实例时,会自动将录入用户写入模型实例
        '''
        serializer.save(owner=self.request.user)


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (IsAdminUserOrReadOnly, )
  • 验证 admin2 用户是有否权限编辑/删除(录入人员且非管理员身份) mydrf18

  • 验证 admin 用户是有否权限编辑/删除(非录入人员但是管理员身份) mydrf19

视图集和路由(**)

使用 ViewSet

ViewSet 类仅在最后一刻被绑定到一组方法处理程序,当它被实例化成一组视图的时候,通常通过使用一个 Router 类来处理自己定义 URL conf 的复杂性

# api/views.py

from rest_framework import viewsets, permissions

from .serializers import SnippetSerializer
from .permissions import IsAdminUserOrReadOnly
from snippets.models import Snippet


class SnippetViewSet(viewsets.ModelViewSet):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (IsAdminUserOrReadOnly, )   # 系统级权限

    def perform_create(self, serializer):
        '''
        重写 perform_create 方法,在保存模型实例时,会自动将录入用户写入模型实例
        '''
        serializer.save(owner=self.request.user)

路由 URLs

使用路由器,将 ViewSets 绑定到 url

# urls.py

from django.contrib import admin
from django.conf.urls import url, include

urlpatterns = [
    ...,
    url(r'api/', include('api.urls')),
    url(r'^api-auth/', include(('rest_framework.urls', 'api-auth'), namespace='rest_framework'))
]


# api/urls.py

from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from . import views

router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet)

app_name='api'
urlpatterns = [
    url(r'', include(router.urls)),   # 添加 router 的 url
]

mydrf20
mydrf21

模式和文档

模式是一种机器可读的文档,描述可用的 API 端点、它们的 URLs 以及它们支持的操作
模式可以是自动生成文档的有用工具,也可以用于驱动可以与 API 交互的动态客户端库

  • 安装 coreapi 包
pip install coreapi
  • 在 URLs 配置中包含一个自动生成的模式视图
# api/urls.py

from rest_framework.schemas import get_schema_view

schema_view = get_schema_view(title='LocalHost API')

urlpatterns = [
    ...,
    url(r'^schema/$', schema_view),
]

mydrf22

  • API 文档
# urls.py

from django.conf.urls import url, include
from rest_framework.documentation import include_docs_urls

urlpatterns = [
    ...,
    url(r'api/docs/', include_docs_urls(title='API Documentation')),   # 文档 url
]

mydrf23

完整的项目文档,请参考 GitHub https://github.com/lichaoxiang/My-Django-REST-Framework

参考文档

http://www.django-rest-framework.org/

原创文章,转载请注明出处:http://www.opcoder.cn/article/9/