《django by example》实践 educa 项目(三)

关键词:django by example


点我查看本文集的说明及目录。


本项目相关内容包括:

实现过程

CH10 创建一个在线学习平台

CH11 缓存内容

CH12 创建API


CH12 创建一个API


上一章,我们创建了学生注册和课程报读系统,创建视图展示了课程内容,并学习了如何使用 Django 的缓存框架。本章,我们将学习如何实现以下功能:

  • 创建 RESTful API
  • 为 API 视图处理授权和权限问题
  • 创建 API 的 viewsets 和 routers

创建一个 RESTful API


我们可能需要创建一个接口来使其它服务与自己的 web 应用进行交互。通过 API 可以实现第三方获得信息以及操作应用程序。

我们可以通过几种方法构建 API ,但是推荐遵守 REST 原则。REST 架构源自 Representational State Transfer 。 RESTful APIs 是基于资源的。模型代表资源,GET、POST、PUT 或 DELETE 等 HTTP 方法可以获取、创建、更新或者删除对象。内容也可以使用 HTTP 响应代码,不同的 HTTP 响应码表示不同的 HTTP 请求结果,比如 2XX 响应码表示成功,4XX 响应码表示失败等。

RESTful API 交换数据时最常使用的格式是 JSON 和 XML 。我们使用 JSON 序列化为项目创建一个 REST API 。 API 将提供以下功能:

  • 获取主题
  • 获取可以获得的课程
  • 获取课程内容
  • 报读课程

我们可以通过创建自定义视图开始学习使用 Django 构建 API 。然而,一些第三方模块可以简化项目创建 API 的过程,这些第三方模块中最受欢迎的是 Django Rest 框架。

安装 django Rest 框架


django Rest 框架帮助用户轻松地创建项目的 REST API 。我们可以从http://www.django-rest-framework.org/找到 REST 框架的所有信息。

打开 shell 并使用以下命令安装框架:

pip install djangorestframework

编辑 educa 项目的 settings.py 文件并在 INSTALLED_APPS 中添加 rest_framework 来激活应用:

INSTALLED_APPS = ['courses', 'django.contrib.admin', 'django.contrib.auth',
                  'django.contrib.contenttypes', 'django.contrib.sessions',
                  'django.contrib.messages', 'django.contrib.staticfiles',
                  'students', 'memcache_status','rest_framework',]

然后,在 settings.py 中添加以下设置:

# REST settings

REST_FRAMEWORK = {'DEFAULT_PERMISSION_CLASSES': [
    'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly']}

我们可以使用 REST_FRAMEWORK 配置 API 。 REST 框架提供很多配置默认行为的设置。 DEFAULT_PERMISSION_CLASSES 设置指定读取、创建、更新或者删除对象的默认权限。 我们将 DjangoModelPermissionsOrAnonReadOnly 设置为唯一的权限类。这个类基于 Django 权限系统,权限系统允许用户创建、更新和删除对象,但匿名用户只能读取对象。后续我们将学习更多的权限。

REST 框架的参数设置列表见http://www.django-rest-framework.org/api-guide/settings/

定义 serializers


设置好 REST 框架后,需要指定数据如何进行序列化。输出数据应该序列化为特定格式,输入数据应该进行反序列化以便于处理。框架提供以下类来进行单一对象的序列化:

  • Serializer:为普通 Python 类实例提供序列化;

  • ModelSerializer:为模型实例提供序列化,使用主键表示对象关系;

  • HyperlinkedModelSerializer:与 ModelSerializer 相同,但是使用链接表示对象关系。

我们来创建第一个 Serializer 。在 courses 应用目录下创建下面的文件结构:

CH12-1.png

我们在 api 目录下创建所有的 API 函数。编辑 serializers.py 文件并添加以下代码:

from rest_framework import serializers

from ..models import Subject


class SubjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = Subject
        fields = ('id', 'title', 'slug')

这是 Subject 模型的 serializer 。serializer 的定义方式与 Form 和 ModelForm 类的定义方式相似。使用 Meta 类来指定进行序列化的模型以及模型包含的字段。如果不设置 fields 属性,则将包含模型所有字段。

我们来测试一下 serializer 。打开命令行并使用 python manage.py shell 打开 Django shell ,运行以下代码:

In [1]: from courses.models import Subject

In [2]: from courses.api.serializers import SubjectSerializer

In [3]: subject = Subject.objects.latest('id')

In [4]: serializer = SubjectSerializer(subject)

In [5]: serializer.data

在这个例子中,我们获取了一个 Subject 对象,创建了一个 SubjectSerializer 实例,并且访问了序列化数据。将得到以下输出:

{'id': 4, 'title': 'Mathematics', 'slug': 'mathematics'}

我们可以看到,模型数据变成了 Python 数据类型。

理解 pasers 和 renderers

HTTP 响应返回序列化数据之前,需要将序列化数据渲染为特定格式。接收到 HTTP 请求时,我们需要解析输入数据并在使用之前对其进行反序列化。 REST 框架包含 renderers 和 parsers 来处理这个过程。

我们来看下如何解析输入数据。对于一个 JSON 字符串输入,可以使用 REST 框架提供的 JSONParser 类来将其转换为 Python 对象。在 Python shell 中执行以下代码:

In [6]: from io import BytesIO

In [7]: from rest_framework.parsers import JSONParser

In [8]: data = b'{"id":4,"title":"Music","slug":"music"}'

In [9]: JSONParser().parse(BytesIO(data))

你应该得到这样的输出:

Out[9]: {u'id': 4, u'slug': u'music', u'title': u'Music'}

REST 框架还包括 Renderer 类来帮助用户格式化 API 响应。框架根据内容确定使用哪个 renderer 。它检查请求的 Accept 头来确定预期的响应内容类型,比如可以由 URL 的格式后缀确定选用的 renderer ,例如,访问将触发 JSONRenderer 来返回 JSON 响应。

回到 shell 并执行以下代码使用前面的序列化例子渲染 serializer 对象:

In [10]: from rest_framework.renderers import JSONRenderer

In [11]: JSONRenderer().render(serializer.data)

输出应该是:

Out[11]: b'{"id":4,"title":"Mathematics","slug":"mathematics"}'

这里使用 JSONRenderer 将序列化数据渲染为 JSON 格式。默认情况下,REST 框架使用两种不同的 renderers:JSONRenderer 和 BrowsableAPIRenderer 。BrowsableAPIRenderer 提供便于浏览 API 的 web 接口。我们可以通过设置 REST_FRAMEWORK 的 DEFAULT_RENDERCLASSES 来更改默认的 renderer 类。

http://www.django-rest-framework.org/api-guide/renderers/http://www.django-rest-framework.org/api-guide/parsers/中有更多关于 renderers 和 parsers 的信息。

创建列表和详情视图


REST 框架内置创建 API 视图的通用视图和 mixin 集合来实现获取、创建、更新或者删除模型对象的功能。http://www.django-rest-framework.org/api-guide/generic-views/中包括 REST 框架提供的所有通用 mixin 和视图。

我们来创建获取 Subject 对象的列表和详情视图。在 courses/api/ 目录下新建一个 views.py 的文件,并添加以下代码:

from rest_framework import generics

from .serializers import SubjectSerializer
from ..models import Subject


class SubjectListView(generics.ListAPIView):
    queryset = Subject.objects.all()
    serializer_class = SubjectSerializer


class SubjectDetailView(generics.RetrieveAPIView):
    queryset = Subject.objects.all()
    serializer_class = SubjectSerializer

代码中使用了 REST 框架的通用 ListAPIView 和 RetrieveAPIView。详细视图中包含一个获取给定键对象的 pk URL参数。两个视图都设置了以下属性:

  • queryset: 获取对象使用的基础 QuerySet ;
  • serializer_class:序列化对象的类;

下面为视图添加 URL模式。在 courses/api/ 目录下新建 urls.py 的文件并添加以下代码:

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^subjects/$', views.SubjectListView.as_view(), name='subject_list'),
    url(r'^subjects/(?P<pk>\d+)/$', views.SubjectDetailView.as_view(),
        name='subject_detail'), ]

编辑 educa 项目的 urls.py 文件并包含以下 API 模式:

url(r'^api/',include('courses.api.urls'))

使用 python manage.py runserver 运行开发服务器,打开 shell 并通过 cURL 获取 http://127.0.0.1:8000/api/subjects/

curl http://127.0.0.1:8000/api/subjects/

你将得到类似下面的输出:

[{"id":4,"title":"Mathematics","slug":"mathematics"},{"id":3,"title":"Music","slug":"music"},{"id":2,"title":"Physics","slug":"physics"},{"id":1,"title":"Programming","slug":"programming"}]

HTTP响应包含 JSON 格式的 Subject 对象列表。如果你的操作系统没有安装 curl,可以从http://curl.haxx.se/dlwiz/下载。除了 curl ,我们还可以使用其它工具发送 HTTP请求,比如 Postman 浏览器插件(可以从 https://www.getpostman.com 获取)。

在浏览器中打开 http://127.0.0.1:8000/api/subjects/ ,你将看到 REST 框架可以浏览的 API :

CH12-2.png

这个 HTML接口由 BrowsableAPIRenderer 提供。它展示了得到的标题和内容,并且允许用户实现请求。我们可以通过在 URL 中添加 id 来访问 Subject 对象的 API 详情视图。在浏览器中打开http://127.0.0.1:8000/api/subjects/1/,你将看到渲染为 JSON 格式的单个对象。

创建嵌套 serializers


我们将为 Course 模型创建 serializer ,编辑 api/serializers.py 文件并添加以下代码:

from ..models import Course

class CourseSerializer(serializers.ModelSerializer):
    class Meta:
        model = Course
        fields = (
        'id', 'subject', 'title', 'slug', 'overview', 'created', 'owner',
        'modules')

我们来看下如何实现 Course 对象序列化,打开 shell ,运行 python manage.py shell ,并运行以下代码:

In [1]: from rest_framework.renderers import JSONRenderer

In [2]: from courses.models import Course

In [3]: from courses.api.serializers import CourseSerializer

In [4]: course = Course.objects.latest('id')

In [5]: serializer = CourseSerializer(course)

In [6]: JSONRenderer().render(serializer.data)

我们将会看到包含 CourserSerializer 设置的字段的 JSON 对象。

b'{"id":2,"subject":4,"title":"Course 2","slug":"course2","overview":"","created":"2018-04-16T15:43:35.885525Z","owner":1,"modules":[5,6]}'

结果中的 modules 管理器的相关对象序列化为主键列表:

"modules":[7,8]

我们希望增加更多模信息,因此需要序列化 Module 对象并进行嵌套。将刚刚添加到 api/serializers.py 中的代码修改为:

from ..models import Course, Module


class ModuleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Module
        fields = ('order', 'title', 'description')


class CourseSerializer(serializers.ModelSerializer):
    modules = ModuleSerializer(many=True, read_only=True)

    class Meta:
        model = Course
        fields = (
            'id', 'subject', 'title', 'slug', 'overview', 'created', 'owner',
            'modules')

这里定义了 ModuleSerializer 来对 Module 模型进行序列化。然后将 modules 属性添加到 CourseSerializer 中来嵌套 ModuleSerializer 。设置 many = True 表示对多个对象进行序列化。read_only 参数表示这个字段是只读的,不能使用任何输入来创建或者修改对象。

打开 shell 并再次创建 CourseSerializer 实例,使用 JSONRenderer 渲染序列化数据。这次,模块列表使用了嵌套的 ModuleSerializer 进行了序列化:

"modules":[{"order":0,"title":"Installing Django","description":"how to install django"},{"order":1,"title":"models","description":"about django model"}]

更多序列化的相关信息见http://www.django-rest-framework.org/api-guide/serializers/

创建自定义视图


REST 框架提供一个 APIView 类,可以基于 Django View 类实现 API 功能。 APIView 与 View 的区别在于使用 REST 框架 自定义的 Request 和 Response 对象并处理 APIException 异常来返回合适的 HTTP 响应。它还包含内置的授权和权限系统来管理视图访问。

我们将为用户报读课程创建一个视图,编辑 api/views.py 文件并添加以下代码:

from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from ..models import Course


class CourseEnrollView(APIView):
    def post(self, request, pk, format=None):
        course = get_object_or_404(Course, pk=pk)
        course.students.add(request.user)
        return Response({'enrolled': True})

CourseEnrollView 视图处理课程的用户报读事务,上面的代码实现以下功能:

  • 创建自定义的 APIView 子类;
  • 为 POST 请求定义 post() 方法,这个视图不允许其它方法;
  • 使用 pk URL 参数来表示课程 ID 。获取给定 pk 参数的课程,如果没有对象课程则引发 404 异常;
  • 将当前的用户添加到 Course 对象的 students 多对多关系中并返回成功响应。

编辑 api/urls.py 文件并为 CourseEnrollView 添加以下 URL 模式:

url(r'^courses/(?P<pk>\d+)/enroll/$', views.CourseEnrollView.as_view(),
    name='course_enroll')

理论上,现在已经可以通过 POST 请求来报读课程,然而,我们还需要识别用户并阻止没有授权的用户访问视图。下面来看下 API 的授权和权限如何工作。

处理授权


REST 框架通过授权类来识别请求用户。如果授权成功,框架将 request.user 设置为授权的 User 对象,如果用户没有授权,Request.user 将设置为 Django AnonymousUser 实例。

REST 框架提供以下授权后端:

  • BasicAuthentication : HTTP 基本授权,客户端在 Authorization HTTP头中发送经过 Base64 编码处理的用户名和密码。https://en.wikipedia.org/wiki/Basic_access_authentication中有详细介绍。

  • TokenAuthertication: 令牌授权。使用 Token 模型来保存用户令牌。通过放在 Authorization HTTP 头中的 token 进行授权。

  • SessionAuthertication:使用 Django 会话后端进行授权。这个后端用于从网站前端向 API 实现授权的 AJAX 请求。

我们可以通过继承 REST 框架提供的 BaseAuthorization 类创建一个自定义授权后端,并覆盖 authenticate() 方法。

每个视图都可以进行授权,也可以使用 DEFAULT_AUTHENTICATION_CLASSES 设置全局授权。

注意:

授权仅能识别请求用户。不能允许或者拒绝访问视图。我们需要使用权限来限制对视图的访问。

我们可以从http://www.django-rest-framework.org/api-guide/authentication/ 找到授权的所有信息。

下面将在视图中添加 BasicAuthentication 。编辑 courses 应用的 api/views.py 并向 CourseEnrollView 添加 authentication_class 属性:

from rest_framework.authentication import BasicAuthentication


class CourseEnrollView(APIView):
    authentication_classes = (BasicAuthentication,)

    def post(self, request, pk, format=None):
        course = get_object_or_404(Course, pk=pk)
        course.students.add(request.user)
        return Response({'enrolled': True})

这里将通过 HTTP 请求头中的 Authorization 标头凭证来识别用户。

为视图添加权限


REST 框架使用权限系统来限制视图访问。REST 框架内置的权限包括:

  • AllowAny : 没有访问限制,用户授权与否都可以访问;
  • IsAuthenticated : 只允许授权的用户访问;
  • IsAuthenticatedOrReadOnly : 授权用户可以进行完全访问,匿名用户只能进行读操作(如 GET 、 HEAD、或 OPTIONS 操作);
  • DjangoModelPermissions : django.contrib.auth 绑定的权限,视图需要设置 queryset 属性。 只有有模型访问权限的用户才能访问。
  • DjangoObjectPermissions : 基于对象的 Django 权限。

如果拒绝用户访问,我们通常获得以下 HTTP 错误码:

  • HTTP 401 :没授权
  • HTTP 403:权限拒绝

我们可以从http://www.django-rest-framework.org/api-guide/permissions/看到更多关于权限的介绍。

编辑 courses 应用的 api/views.py 文件并为 CourseEnrollView 添加 permission_class 属性:

from rest_framework.permissions import IsAuthenticated


class CourseEnrollView(APIView):
    authentication_classes = (BasicAuthentication,)
    permission_classes = (IsAuthenticated,)

这里添加了 IsAuthorization 权限,将阻止匿名用户访问视图,现在可以向 API 发送 POST 请求了。

保证开发服务器正在运行,打开 shell 并运行以下命令:

curl -i -X POST http://127.0.0.1:8000/api/courses/1/enroll/

你将得到这样的响应:

HTTP/1.0 401 Unauthorized
Date: Mon, 05 Mar 2018 07:00:42 GMT
Server: WSGIServer/0.1 Python/2.7.10
Content-Length: 58
Vary: Accept
Allow: POST, OPTIONS
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
WWW-Authenticate: Basic realm="api"

{"detail":"Authentication credentials were not provided."}

这里接收到了预期的 401 HTTP 码,这是由于我们没有授权。使用一个有基本权限的用户,运行以下命令:

curl -i -X POST -u student:password http://127.0.0.1:8000/api/courses/1/enroll/

使用注册过的用户的凭证代替 student:password ,将会看到下面的输出:

HTTP/1.0 200 OK
Date: Mon, 05 Mar 2018 07:07:50 GMT
Server: WSGIServer/0.1 Python/2.7.10
Vary: Accept
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
Content-Length: 17
Allow: POST, OPTIONS

{"enrolled":true}

我们可以访问 admin 网站并检查用户是否报读了课程。

创建视图集合和routers

ViewSets 帮助我们定义 API 与 REST 框架(通过 Router 对象)动态创建的 URLs 的交互。使用视图集合,可以避免多个视图使用重复逻辑。视图集合包含 list()、create()、retrieve()、update()、partial_update() 和 destroy() 等实现获取、更新、删除等操作动作的方法。

下面为 Course 模型创建一个视图集合,编辑 api/views.py 并添加以下代码:

from rest_framework import viewsets
from .serializers import CourseSerializer


class CourseViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer

创建 ReadOnlyModelViewSet 子类,ReadOnlyModelViewSet 提供只读权限的 list() 和 retrieve() 操作来列出对象集合或获取单个对象。编辑 api/urls.py 并为视图集合创建 router :

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

from . import views

router = routers.DefaultRouter()
router.register('courses', views.CourseViewSet)

urlpatterns = [
    url(r'^subjects/$', views.SubjectListView.as_view(), name='subject_list'),
    url(r'^subjects/(?P<pk>\d+)/$', views.SubjectDetailView.as_view(),
        name='subject_detail'),
    url(r'^courses/(?P<pk>\d+)/enroll/$', views.CourseEnrollView.as_view(),
        name='course_enroll'), 
    url(r'^', include(router.urls)), ]

这里创建了 DefaultRouter 对象并使用 courses 前缀注册视图集合,router 负责为视图集合动态生成 URLs 。

在浏览器中打开 http://127.0.0.1:8000/api/,将会看到 router 在 URL 中列出所有视图集合,如下图所示:

CH12-3.png

可以访问 http://127.0.0.1:8000/api/courses/ 来获得课程列表。

可以从 http://www.django-rest-framework.org/api-guide/viewsets/了解更多关于视图集合的消息。可以从http://www.django-rest-framework.org/api-guide/routers/找到更多关于 router 的消息。

为视图集合添加额外动作


Viewsets 还可以添加额外动作。我们来将 CourseEnrollView 视图更改为自定义视图集合动作。编辑

api/views.py 文件并更改 CourseViewSet :

from rest_framework.decorators import action

class CourseViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer

    @action(methods=['post'], detail=True,
            authentication_classes=[BasicAuthentication],
            permission_classes=[IsAuthenticated])
    def enroll(self, request, *args, **kwargs):
        course = self.get_object()
        course.students.add(request.user)
        return Response({'enrolled': True})

笔者注:

原文代码为:

from rest_framework.decorators import detail_route


class CourseViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer

    @detail_route(methods=['post'],
                  authentication_classes=[BasicAuthentication],
                  permission_classes=[IsAuthenticated])
    def enroll(self, request, *args, **kwargs):
        course = self.get_object()
        course.students.add(request.user)
        return Response({'enrolled': True})

由于 rest_framework 3.10 版本之后将弃用 detail_route,这里使用 action( detail=True )代替了原文中的 detail_route。

这里添加了自定义 enroll() 方法实现视图集合的额外动作,上面的代码功能为:

  • 使用框架的 action 装饰器指定这是对单个对象进行操作的动作;

  • 可以使用装饰器为动作添加自定义属性,这里指定只有 POST 方法可以访问视图,并且设置了授权类和权限类;

  • 使用 self.get_object() 来获得 Course 对象;

  • 将当前用户添加到 students 多对多关系中并返回自定义成功响应。

编辑 api/urls.py 文件并删除下面的 URL ,因为不再需要这个链接:

url(r'^courses/(?P<pk>\d+)/enroll/$', views.CourseEnrollView.as_view(),
    name='course_enroll'), 

然后编辑 api/views.py 文件并删除 CourseEnrollView 类。

现在,报读课程的链接可以通过 router 动态生成,由于使用名为 enroll 的动作自动生成,生成的 URL 与之前的一样。

创建自定义权限


我们期望学生能够访问报读了的课程内容。只有报读了课程的学生才能访问内容。实现这个功能的最好方法是自定义权限类。rest_framework 提供 BasePermission 类来帮助用户定义以下方法:

  • has_permission() :视图级别的权限检查;
  • has_object_permission() :实例级别的权限检查;

这些方法返回 True 表示成功,返回 False 表示失败。在 courses/api/ 目录下新建 permissions.py 的文件,并添加以下代码:

from rest_framework.permissions import BasePermission


class IsEnrolled(BasePermission):
    def has_object_permission(self, request, view, obj):
        return obj.students.filter(id=request.user.id).exists()

创建 BasePermission 的子类并重写 has_object_permission() 方法。检查请求的用户是否在 Course 对象的 students 关系中,下一步我们将使用 IsEnrolled 权限。

序列化课程内容


我们需要序列化课程内容, Content 模型内置通用外键来访问不同类型内容模型。前一章,我们还为所有方法模型添加了自定义 render() 方法。这个方法可以为 API 提供渲染的内容。

编辑 courses 应用的 api/serializers.py 文件并添加以下代码:

from ..models import Content


class ItemRelatedField(serializers.RelatedField):
    def to_representation(self, value):
        return value.render()


class ContentSerializer(serializers.ModelSerializer):
    item = ItemRelatedField(read_only=True)

    class Meta:
        model = Content
        fields = ('order', 'item')

上面的代码,继承 REST 框架提供的 RelatedField 序列化字段创建自定义字段,并重写 to_representation() 方法。为 Content 模型定义 ContentSerializer 并使用自定义字段表示通用外键 item 。

Module 模型还需要创建序列化器来包含课程内容并扩展的 Course 序列化器。编辑 api/serializers.py 文件并添加以下代码:

class ModuleWithContentsSerializer(serializers.ModelSerializer):
    contents = ContentSerializer(many=True)

    class Meta:
        model = Module
        fields = ('order', 'title', 'description', 'contents')


class CourseWithContentsSerializer(serializers.ModelSerializer):
    modules = ModuleWithContentsSerializer(many=True)

    class Meta:
        model = Course
        fields = (
        'id', 'subject', 'title', 'slug', 'overview', 'created', 'owner',
        'modules')

我们来创建一个模仿 retrieve() 课程内容动作的视图。编辑 api/views.py 文件并在 CourseViewSet 类中添加以下方法:

from .permissions import IsEnrolled
from .serializers import CourseWithContentsSerializer


class CourseViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer

    @action(methods=['post'], detail=True,
            authentication_classes=[BasicAuthentication],
            permission_classes=[IsAuthenticated])
    def enroll(self, request, *args, **kwargs):
        course = self.get_object()
        course.students.add(request.user)
        return Response({'enrolled': True})

    @action(methods=['get'], serializer_class=CourseWithContentsSerializer,
            authentication_classes=[BasicAuthentication],
            permission_classes=[IsAuthenticated])
    def contents(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

这个方法的描述如下:

  • 使用 detail_route 装饰器指定这个动作应用于单个对象;

  • 指定只有 GET 方法可以访问这个动作;

  • 使用 CourseWithContentsSerializer 序列化类包含渲染的课程内容;

  • 使用 IsAutheticated 和自定义 IsEnrolled 权限指定动作权限,这样可以保证只有订阅了课程的用户才能访问内容。

  • 使用存在的 retrieve() 动作返回课程对象。

在浏览器中打开 http://127.0.0.1:8000/api/courses/1/contents/,如果使用具有权限的用户访问视图,将可以看到课程的每个模块,模块包含渲染的 HTML 内容:

CH12-4.png

我们已经创建了一个帮助其它服务程序访问课程应用的简单 API 。REST 框架可以使用 ModelViewSet 视图集来管理创建和编辑对象。我们已经介绍了 Django Rest 框架的主要内容,更多扩展内容详见http://www.django-rest-framework.org/

总结


本章,我们创建了一个 RESTful API 供其它服务与 web 应用交互。

第 13章 上线运行 可以从https://www.packtpub.com/sites/default/files/downloads/Django_By_Example_GoingLive.pdf下载。它将教我们如何通过 uWSGI 和 NGINX 创建生产环境,以及如何实现自定义 middleware 和创建自定义管理命令。

你已经到达了本书的结尾。恭喜你!已经学习了使用 Django 成功地创建 web 应用的技能。这本书引导你开发实际生活中需要的项目以及使用 Django 集成其它技术。现在,你已经为创建自己的 Django 项目做好了准备,不管它是简单的原型还是大型的 web应用程序。

祝下一次的 Django 冒险成功。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容