大望路网站建设经典的企业网站

当前位置: 首页 > news >正文

大望路网站建设,经典的企业网站,互联网推广是什么工作,电商设计是干嘛的第十二章 创建API在上一章里#xff0c;创建了一个学生注册系统和选课系统。然后创建了展示课程内容的视图#xff0c;以及学习了如何使用Django缓存框架。在这一章里有如下内容#xff1a;建立RESTful API管理API视图的认证与权限建立API视图集和路由1创建RESTful API你可能…第十二章 创建API在上一章里创建了一个学生注册系统和选课系统。然后创建了展示课程内容的视图以及学习了如何使用Django缓存框架。在这一章里有如下内容建立RESTful API管理API视图的认证与权限建立API视图集和路由1创建RESTful API你可能会想建立一个接口API让其他应用程序和我们的网站进行交互。通过建立一个API就可以让第三方应用程序自动化的操作和消费我们网站生产的数据。译者注:使用API而不是模板渲染与前端进行交互这就是前后端分离的思路。仅使用Django来进行Web开发的话前后端分离并不明显。读者在未来的Web开发中接触到前端框架就会对此有更深的了解。有很多种方式可以建立这样一个API推荐根据REST原则来建立这样一个API。REST是Representational State Transfer的简称。RESTful API是基于资源的即URL用于表示网站所有的资源HTTP的请求种类比如GET,POST,PUT或DELETE表示对应的行为即获取创建更新和删除数据。不同的HTTP响应码表示这次动作的完成结果例如2XX表示该操作成功4XX表示错误等。RESTful API常用的数据交换格式是JSON或者XML我们准备建立一个使用JSON进行数据交换的API。我们的API会提供以下功能获取主题获取可用的课程获取课程内容在一个课程中注册我们可以从0开始写视图来建立该API也可以通过第三方应用简单的为项目建立API在这方面最出名的第三方应用就是Django REST framework。1.1安装Django REST frameworkDjango REST framework可以让你简单地创建符合REST风格的API其官方网站是https://www.django-rest-framework.org/。打开系统命令行输入如下命令Copypip install djangorestframework3.8.2然后编辑settings.py激活rest_framework应用CopyINSTALLED_APPS [# …rest_framework, ]再在settings.py中加入如下设置CopyREST_FRAMEWORK {DEFAULT_PERMISSION_CLASSES: [rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly,] }原书代码这里少了一个左方括号。REST_FRAMEWORK用于具体设置该模块。REST framework提供了很多设置DEFAULT_PERMISSION_CLASSES提供了对于增删改查行为的默认权限。我们设置了DjangoModelPermissionsOrAnonReadOnly作为唯一默认的权限类。这个权限类依赖于Django的权限系统让用户可以增删改查数据对象同时让未登录用户只能进行只读操作。在下边的向视图增加权限一节中还会详细学习这部分功能。Django REST 框架的全部设置可以在https://www.django-rest-framework.org/api-guide/settings/找到。1.2设置序列化器在设置好框架后还需要确定使用的序列化器。网站对外提供的数据应当是经过序列化的标准数据同时还需要对外界输入的数据进行反序列化。REST框架提供了下列类用于对一个单独对象设置序列化器Serializer为普通的Python类实例提供序列化ModelSerializer为数据模型的实例提供序列化HyperlinkedModelSerializer与ModelSerializer的功能相同但可以通过超链接来表示对象之间的关系而不是通过主键关联。让我们来实际建立一个序列化器。在courses应用中建立如下文件结构Copyapi/__init__.pyserializers.py我们创建了一个叫做api的包然后打算在这个包里建立序列化器。编辑serializers.py文件添加下列代码Copyfrom rest_framework import serializers from ..models import SubjectclassSubjectSerializer(serializers.ModelSerializer):classMeta:model Subjectfields [id, title, slug] 这是继承了ModelSerializer类的专门用于Subject模型的序列化器。定义序列化器的类使用起来和Form以及ModelForm类很类似Meta内的属性允许指定要序列化的类及字段。如果不设置具体的fields属性则默认会包含该模型的全部字段。来实验一下这个序列化器进入带有Django环境的Python命令行模式Copypython manage.py shell输入以下命令Copy from courses.models import Subjectfrom courses.api.serializers import SubjectSerializersubject Subject.objects.latest(id)serializer SubjectSerializer(subject)serializer.data {id: 4, title: Programming, slug: programming}在这个例子里先获取了一个Subject实例然后创建了一个序列化器的实例SubjectSerializer并访问序列化之后的数据可以看到模型的数据被序列化成了Python原生的字典类型数据。1.3理解解析器parser与渲染器renderer序列化的结果在通过HTTP响应返回之前必须被渲染成为一个特殊的格式。同样在从HTTP请求中获取数据的时候也必须解析数据然后反序列化。REST框架包含了渲染器和解析器用于处理这些过程。先来看看如何解析数据在Python命令行模式中输入下列命令Copy from io import BytesIOfrom rest_framework.parsers import JSONParserdata b{id:4,title:Programming,slug:programming} JSONParser().parse(BytesIO(data)) {id: 4, title: Programming, slug: programming}可以看到给定一个二进制字节流形式的JSON字符串使用JSONParser可以将其反序列化为Python的数据对象。REST框架还包含渲染器Renderer类用于格式化API的响应。框架通过上下文内容协商机制来确定使用哪种渲染器即渲染器会通过HTTP请求的Accept头部字段来确定这个请求所需要的内容类型来进行判断。还可以通过URL的格式化的前缀来判断例如一个请求返回JSON格式响应的访问可能会触发JSONRenderer渲染器。再回到Python命令行模式中在刚才的代码的基础上继续执行下列代码Copy from rest_framework.renderers import JSONRendererJSONRenderer().render(serializer.data) b{id:4,title:Programming,slug:programming}使用JSONRenderer可以将Python数据对象渲染成JSON字符串。REST框架提供了两个不同的渲染器JSONRenderer和BrowsableAPIRenderer。后者提供了一个浏览API返回数据的web界面。可以在settings.py的REST_FRAMEWORK设置中的DEFAULT_RENDERER_CLASSES选项中设置默认的渲染器。关于渲染器和解析器的更多说明可以看https://www.django-rest-framework.org/api-guide/renderers/和https://www.django-rest-framework.org/api-guide/parsers/。1.4创建列表和详情视图REST框架包含一系列内置的通用视图和mixins用于建立API视图提供了增删改查数据模型对象的功能。关于所有的通用视图和mixin可以看https://www.django-rest-framework.org/api-guide/generic-views/。现在来建立一个获取Subject对象的视图在courses/api/目录内新建views.py文件在其中增加下列代码Copyfrom rest_framework import generics from ..models import Subject from .serializers import SubjectSerializerclassSubjectListView(generics.ListAPIView):queryset Subject.objects.all()serializer_class SubjectSerializerclassSubjectDetailView(generics.RetrieveAPIView):queryset Subject.objects.all()serializer_class SubjectSerializer在这段代码中使用了REST框架提供的ListAPIView和RetrieveAPIView两个内置视图在URL中包含一个主键参数用于获取具体的数据对象。两个视图都有下列属性queryset基础的QuerySet用于返回数据serializer_class序列化器对象指定要使用的序列化器接下来为新的视图配置URL在courses/api/下新建urls.py文件然后编辑其中的内容Copyfrom django.urls import path from . import viewsapp_name coursesurlpatterns [path(subjects/, views.SubjectListView.as_view(), namesubject_list),path(subjects/pk/, views.SubjectDetailView.as_view(), namesubject_detail), ]然后编辑educa项目的根urls.py加上一行Copyurlpatterns [# ……path(api/, include(courses.api.urls, namespaceapi)), ]我们为API视图使用了api路由命名空间。启动站点使用curl命令访问http://127.0.0.1:8000/api/subjects/Copycurl http://127.0.0.1:8000/api/subjects/会得到和下边很相似的响应Copy[{id:1,title:Mathematics,slug:mathematics},{id:2,title:Music,slug:music},{id:3,title:Physics,slug:physics},{id:4,title:Programming,slug:programming} ]这个HTTP响应包含一系列JSON格式的字符串其内容是序列化后的所有Subject模型中的数据包含指定的三个字段。如果系统中没有安装curl可以通过 https://curl.haxx.se/dlwiz/ 进行安装。也可以通过其他浏览器扩展比如Postman在https://www.getpostman.com/进行安装。现在不使用curl而是直接在浏览器中打开http://127.0.0.1:8000/api/subjects/会看到如下页面这个界面就是由之前提到的BrowsableAPIRenderer渲染器提供的。页面内显示了结果的头部信息及API的返回信息。还可以通过在URL中包含具体的ID来获取一个Subject对象访问http://127.0.0.1:8000/api/subjects/1/可以发现页面只展示了一个单独的JSON格式的对象数据。1.5创建嵌套的序列化器我们再为Course模型创建一个序列化器打开courses/api/serializers.py继续编辑Copyfrom ..models import CourseclassCourseSerializer(serializers.ModelSerializer):classMeta:model Coursefields [id, subject, title, slug, overview, created, owner, modules] 之后看一下Course序列化器是如何工作的进入Python命令行模式输入下列命令Copy from rest_framework.renderers import JSONRendererfrom courses.models import Coursefrom courses.api.serializers import CourseSerializercourse Course.objects.latest(id)serializer CourseSerializer(course)JSONRenderer().render(serializer.data)这个时候可以看到查询结果里该课程包含的模块是一个主键列表的形式类似这样Copymodules: [6, 7, 9, 10]这样的数据意义不大我们想在结果里包含每个Module的更多信息所以还必须给Module模型也制作一个序列化器编辑serializers.py修改成如下Copyfrom rest_framework import serializers from ..models import ModuleclassModuleSerializer(serializers.ModelSerializer):classMeta:model Modulefields [order,title,description]classCourseSerializer(serializers.ModelSerializer):modules ModuleSerializer(manyTrue, read_onlyTrue)classMeta:model Coursefields [id, subject, title, slug, overview, created, owner, modules]首先为Module模型制作了一个序列化器然后给CourseSerializer增加了一个modules属性设置为Module类的序列化器manyTrue表示需要序列化多个对象read_only参数表示这个字段是只读的不应该被包含在任何需要进行增删改的字段中。重新启动Python命令行模式再执行一遍上边Python命令行代码使用JSONRenderer渲染序列化器实例的data属性可以看到结果中关于modules的部分被嵌套的ModuleSerializer序列化成下面这样Copymodules: [{order: 0,title: Introduction to overview,description: A brief overview about the Web Framework.},{order: 1,title: Configuring Django,description: How to install Django.},… ]这样就完成了嵌套序列化的工作关于序列化器的更多信息可以看https://www.django-rest-framework.org/api-guide/serializers/。1.6创建自定义API视图REST框架提供了一个APIView视图基于Django内置的View视图基础上增加了RESTful API的功能但与View不同的是APIView采用了REST框架自定义的处理Request和Response对象的方法并且在返回HTTP响应的时候处理APIException错误而且还包含内建的认证系统来管理对视图的访问。下边通过APIView来创建一个视图供用户选课编辑courses应用的api/views.py文件增加如下代码Copyfrom django.shortcuts import get_object_or_404 from rest_framework.views import APIView from rest_framework.response import Response from ..models import CourseclassCourseEnrollView(APIView):defpost(self, request, pk, formatNone):course get_object_or_404(Course, pkpk)course.students.add(request.user)return Response({enrolled: True})这个CourseEnrollView视图管理用户选课的功能。代码解释如下:创建一个视图继承APIView在其中定义了post()方法用于处理POST请求这个类不需要处理其他类型的HTTP请求。这个类需要接收一个pk参数为课程的主键id用于取得该课程对象。如果找不到就返回404错误。添加当前用户与Course对象的多对多关系即选课。编辑api/urls.py文件为新的视图配置URLCopypath(courses/pk/enroll/, views.CourseEnrollView.as_view(), namecourse_enroll),现在理论上我们就可以发送一个POST请求来选课而无需在页面中点击按钮。然而这么做需要区分用户身份避免未认证的用户也来发送POST请求。下一节来看看API认证与权限管理是如何工作的。1.7处理身份认证REST框架提供了一个认证类用于鉴别提交HTTP请求的用户身份。如果认证通过REST框架会在request.user中设置认证后的User对象如果没有用户通过认证则request被设置一个Django内置的AnonymousUser对象。REST框架提供如下的认证后端BasicAuthentication这是基础的HTTP认证BA认证用户和密码存放在HTTP请求头的Authorization头部数据中以Base64格式发送。关于BA认证的具体内容看这里。TokenAuthentication这是基于token的认证一个Token模型用于存放用户的tokenHTTP请求头中的Authorization信息中存储token数据用于验证。SessionAuthentication使用Django的session后端进行验证对于前端发来的AJAX请求一般使用该方式验证。RemoteUserAuthentication允许使用web服务器代理认证会设置一个REMOTE_USER变量。除此之外还可以继承REST框架提供的BaseAuthentication类并且重写authenticate()方法来创建自定义的验证后端。通过DEFAULT_AUTHENTICATION_CLASSES还可以设置认证是基于每个视图的还是全局认证。认证Authentication只解决用户身份的问题即识别发请求的用户身份但不会允许或阻止用户访问视图必须通过设置用户权限来限制访问视图。在https://www.django-rest-framework.org/api-guide/authentication/可以找到所有认证相关的文档。在视图中增加BasicAuthentication类编辑api/views.py文件为CourseEnrollView添加一行Copyfrom rest_framework.authentication import BasicAuthenticationclassCourseEnrollView(APIView):authentication_classes (BasicAuthentication,)# ……现在视图就可以通过HTTP请求头的Authorization头部信息进行用户身份认证了。1.8为视图增加权限控制REST框架提供了一个权限系统用于控制对视图的访问。REST框架内建的部分权限有AllowAny完全开放权限不管用户认证与否都不做任何限制IsAuthenticated仅允许通过认证的用户IsAuthenticatedOrReadOnly认证用户具有完整权限匿名用户只读只能使用GETHEADOPTIONS三种HTTP请求种类。DjangoModelPermissions使用django.contrib.auth的权限管理系统。视图需要一个queryset属性只有认证的用户加上具备访问某个数据类的权限才能够进行操作DjangoObjectPermissions也使用Django权限是基于每个对象的单独权限设置。如果用户因为权限问题操作失败则通常会得到下列HTTP响应码和错误信息HTTP 401: UnauthorizedHTTP 403Permission denied可以在https://www.django-rest-framework.org/api-guide/permissions/中找到更多关于权限的信息。继续编辑api/views.py文件为CourseEnrollView添加一个属性permission_classesCopyfrom rest_framework.authentication import BasicAuthentication from rest_framework.permissions import IsAuthenticatedclassCourseEnrollView(APIView):authentication_classes (BasicAuthentication,)permission_classes (IsAuthenticated,)# ……我们为视图加上了IsAuthenticated权限意味着只有认证用户可以访问该视图。现在可以尝试给这个视图发一个POST请求。启动站点然后在系统命令行里输入下列命令Copycurl -i -X POST http://127.0.0.1:8000/api/courses/1/enroll/应该会得到下列响应CopyHTTP/1.1401 Unauthorized …… {detail:Authentication credentials were not provided.}结果得到了401响应因为我们没有认证过。现在我们为请求头增加一个已经注册的用户的认证信息将下列代码中的student:password替换成你网站中的实际用户名和密码然后执行命令Copycurl -i -X POST -u student:password http://127.0.0.1:8000/api/courses/1/enroll/会得到如下响应CopyHTTP/1.1 200 OK …… {enrolled:true}现在可以到管理后台查看数据库是否已经更新了该用户选课的数据。1.9创建视图集和路由视图集Viewsets允许对API定义一系列的交互动作并允许REST框架使用一个Router对象动态的建立URL。通过使用视图集可以避免重复编写视图逻辑。REST框架中的视图集涵盖的经典的增删改查动作包括list(), create(), retrieve(), update(), partial_update(), 和 destroy()。为Course模型创建一个视图集编辑api/views.py文件增加如下代码Copyfrom rest_framework import viewsets from .serializers import CourseSerializerclassCourseViewSet(viewsets.ReadOnlyModelViewSet):queryset Course.objects.all()serializer_class CourseSerializer创建了一个视图集并继承了ReadOnlyModelViewSetReadOnlyModelViewSet提供了只读的list()和retrieve()方法可以返回一个对象集合或者单个对象。编辑api/urls.py为视图集配置URLCopyfrom django.urls import path, include from rest_framework import routers from . import viewsrouter routers.DefaultRouter() router.register(courses, views.CourseViewSet)urlpatterns [# ……path(, include(router.urls)), ]建立了一个默认的路由对象DefaultRouter()然后将CourseViewSet视图注册到路由中使用了前缀courses现在这个router对象就可以为视图集动态的生成URL。打开http://127.0.0.1:8000/api/可以看到如下页面这个时候可以访问http://127.0.0.1:8000/api/courses/就可以得到JSON格式的课程列表。这个路径中的/courses/就是注册路由的时候使用的前缀courses。视图集的详细使用可以看 https://www.django-rest-framework.org/api-guide/viewsets/路由的使用方法可以参考https://www.django-rest-framework.org/api-guide/routers/。1.10为视图集添加额外功能可以为视图集添加额外功能。让我们来把CourseEnrollView变成一个自定义的视图集。编辑api/views.py文件修改CourseViewSet为下面这样Copyfrom rest_framework.decorators import detail_routeclassCourseViewSet(viewsets.ReadOnlyModelViewSet):queryset Course.objects.all()serializer_class CourseSerializerdetail_route(methods[post], authentication_classes[BasicAuthentication], permission_classes[IsAuthenticated])defenroll(self, request, *args, kwargs):course self.get_object()course.students.add(request.user)return Response({enrolled: True})为视图集增加了一个自定义的方法enroll()代表为视图集增加的新功能解释如下使用detail_router装饰器该装饰器已经被Pycharm提醒要被删除未来改用action装饰器定义了这是一个在单个对象上执行的功能。这个装饰器同时还允许添加参数methods设置为[post]表示该视图只接受POST请求然后还设置了验证和权限。使用self.get_object()获取当前的Course对象。把当前的用户增加到多对多关系中选课返回一个自定义的成功响应然后编辑api/urls.py文件去掉下边这一行因为通过detail_route动态配置了新的路由这一行无需再用Copy/enroll/, views.CourseEnrollView.as_view(), namecourse_enroll),然后编辑api/views.py删除CourseEnrollView因为这个类的功能现在成为视图集的一部分。现在选课功能的URL由router自动生成实际的URL与刚才相同因为使用了我们自定义的函数名称enroll。1.11自定义权限我们希望只有选了某课程的学生用户才可以访问该课程的全部内容。最好的方式就是自定义一个权限REST框架原书为Django应该为REST提供了一个BasePermission类允许重写下列方法has_permission()视图级别的权限检查has_object_permission()对象级别的权限检查这两个方法必须返回True表示具有权限或False表示不具有权限。在courses/api/目录下新建permissions.py文件添加下列代码Copyfrom rest_framework.permissions import BasePermissionclassIsEnrolled(BasePermission):defhas_object_permission(self, request, view, obj):return obj.students.filter(idrequest.user.id).exists()这个IsEnrolled权限继承了BasePermission类然后重写了has_object_permission方法。由于这个方法是基于对象的所以obj就是当前的课程。检查当前用户是否在已经选该课的所有用户里。之后就可以使用该权限类了。1.12序列化课程内容现在已经把主题课程和章节都序列化了。还必须序列化内容。Content模型有一个通用外键关系可以用于检索所有内容模型。而且我们还为所有内容模型添加了render()方法。可以使用这些关系和方法来实现序列化。编辑api/serializers.py文件添加下列代码Copyfrom ..models import ContentclassItemRelatedField(serializers.RelatedField):defto_representation(self, value):return value.render()classContentSerializer(serializers.ModelSerializer):item ItemRelatedField(read_onlyTrue)classMeta:model Contentfields [order, item]在这段代码里通过继承RelatedField定义了一个特别的字段ItemRelatedField然后重写了to_representation()方法。然后定义了内容序列化器ContentSerializer并且指定与原来通用外键同名的item属性为刚定义的ItemRelatedField字段。我们还需要另外一个用于Module模型的序列化器其中嵌套这个Content序列化器还需要改造Course序列化器以让其也包含内容输出编辑api/serializers.py文件添加下列代码CopyclassModuleWithContentsSerializer(serializers.ModelSerializer):contents ContentSerializer(manyTrue)classMeta:model Modulefields [order, title, description, contents]classCourseWithContentsSerializer(serializers.ModelSerializer):modules ModuleWithContentsSerializer(manyTrue)classMeta:model Coursefields [id, subject, title, slug, overview, created, owner, modules]这其实就是一层一层从内到外嵌套序列化器由于已经定义了Content的序列化器就建立一个外层的ModuleWithContent序列化器其中设置contents字段为Content序列化器再往上一层的CourseWithContent序列化器也是类似来嵌套ModuleWithContent。再建立一个视图模仿刚才的retrieve()行为但是采用新的序列化器编辑api/views.py文件给CourseViewSet视图集添加下列代码Copyfrom .permissions import IsEnrolled from .serializers import CourseWithContentsSerializerclassCourseViewSet(viewsets.ReadOnlyModelViewSet):# … detail_route(methods[get],serializer_classCourseWithContentsSerializer,authentication_classes[BasicAuthentication],permission_classes[IsAuthenticated, IsEnrolled])defcontents(self, request, *args, kwargs):return self.retrieve(request, *args, kwargs)这段代码解释如下使用detail_route装饰器来定义该方法是针对一个单独数据对象的该方法只接受GET请求使用了CourseWithContentsSerializer这个新的序列器用于返回包含具体内容数据的序列化后输出。添加了用户认证IsAuthenticated和自定义权限IsEnrolled采用ReadOnlyModelViewSet提供的retrieve()方法来返回Course对象然后打开http://127.0.0.1:8000/api/courses/1/contents/ 。如果你的当前用户选了对应的课程就可以看到课程章节和内容嵌套渲染后的字符串以JSON的形式显示出来类似下边这样Copy{order: 0,title: Introduction to Django,description: Brief introduction to the Django Web Framework.,contents: [{order: 0,item: pMeet Django. Django is a high-level Python Web framework…/p},{order: 1,item: \niframe width\480\ height\360\ src\http://www.youtube.com/embed/bgV39DlmZ2U?wmodeopaque frameborder\0\ allowfullscreen/iframe\n}] }现在我们就建立了一个简单的符合RESTful风格的API让网站自动化向外部提供数据。REST框架还可以使用ModelViewSet来创建和编辑数据对象。关于REST框架中的主要内容在本章都涉及到了如果对于框架特性还需要详细了解可以参考REST框架的官方文档https://www.django-rest-framework.org/。总结在本章为其他程序自动化使用本网站的程序建立了一套API方便与其他应用程序进行互动。下一章将讨论如何通过uWSGI和NGINX配置生产环境。你还会学到如何实现一个自定义的中间件以及建立自定义的管理命令。如有不懂还要咨询下方小卡片博主也希望和志同道合的测试人员一起学习进步在适当的年龄选择适当的岗位尽量去发挥好自己的优势。我的自动化测试开发之路一路走来都离不每个阶段的计划因为自己喜欢规划和总结测试开发视频教程、学习笔记领取传送门