美文网首页django by example 实践Python
《django by example》实践 educa 项目(三

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

作者: 学以致用123 | 来源:发表于2018-04-21 10:58 被阅读54次

    关键词: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 冒险成功。

    相关文章

      网友评论

      本文标题:《django by example》实践 educa 项目(三

      本文链接:https://www.haomeiwen.com/subject/fcztlftx.html