利用 Django REST framework 编写 RESTful API

利用 Django REST framework 编写 RESTful API

自动生成符合 RESTful 规范的 API

支持 OPTION、HEAD、POST、GET、PATCH、PUT、DELETE

根据Content-Type来动态的返回数据类型(如 text、json)

生成 browserable 的交互页面(自动为 API 生成非常友好的浏览器页面)

非常细粒度的权限管理(可以细粒度到 field 级别)

示意图

利用 Django REST framework 编写 RESTful API_第1张图片

安装

$pipinstalldjangorestframework$pipinstallmarkdown

概述

Django Rest framework 的流程大概是这样的

利用 Django REST framework 编写 RESTful API_第2张图片

建立 Models

依靠 Serialiers 将数据库取出的数据 Parse 为 API 的数据(可用于返回给客户端,也可用于浏览器显示)

ViewSet 是一个 views 的集合,根据客户端的请求(GET、POST等),返回 Serialiers 处理的数据

权限 Premissions 也在这一步做处理

ViewSet 可在 Routers 进行注册,注册后会显示在 Api Root 页上

在 urls 里注册 ViewSet 生成的 view,指定监听的 url

希望全面细致了解的人请移步去看官方文档,我这里就不一步步的细说了,而是分块来进行介绍

准备工作 & Models

让我们来写个小项目练练手

先用manage.py startproject rest来生成一个项目

再用manage.py createsuperuser创建用户(后面权限管理会用到)

初始化数据库manage.py migrate

然后当然是编写 models,为了展示 rest_framework 的强大之处,我给 models 定义了一个自定义的 field

# myproject/myapp/models.py#! /usr/bin/env python# -*- coding: utf-8from__future__importunicode_literals,absolute_importimportcPickleaspicklefromdjango.dbimportmodelsfromdjango.contrib.auth.modelsimportUserclassSerializedField(models.TextField):"""序列化域用 pickle 来实现存储 Python 对象"""__metaclass__=models.SubfieldBase# 必须指定该 metaclass 才能使用 to_pythondefvalidate(self,val):raiseisinstance(val,basestring)defto_python(self,val):"""从数据库中取出字符串,解析为 python 对象"""ifvalandisinstance(val,unicode):returnpickle.loads(val.encode('utf-8'))returnvaldefget_prep_value(self,val):"""将 python object 存入数据库"""returnpickle.dumps(val)classMyModel(models.Model):created_at=models.DateTimeField(auto_now_add=True)# 注意这里建立了一个外键owner=models.ForeignKey(User,related_name='mymodels')field=models.CharField(max_length=100)options=SerializedField(max_length=1000,default={})

Serializers

定义好了 Models,我们可以开始写 Serializers,这个相当于 Django 的 Form

# myproject/myapp/serializers.py#! /usr/bin/env python# -*- coding: utf-8from__future__importunicode_literals,absolute_importimportjsonfromdjango.contrib.auth.modelsimportUserfromrest_frameworkimportserializersfrom..modelsimportMyModelfrom.fieldsimportMyCustFieldclassMyCustField(serializers.CharField):"""为 Model 中的自定义域额外写的自定义 Serializer Field"""defto_representation(self,obj):"""将从 Model 取出的数据 parse 给 Api"""returnobjdefto_internal_value(self,data):"""将客户端传来的 json 数据 parse 给 Model"""returnjson.loads(data.encode('utf-8'))classUserSerializer(serializers.ModelSerializer):classMeta:model=User# 定义关联的 Modelfields=('id','username','mymodels')# 指定返回的 fields# 这句话的作用是为 MyModel 中的外键建立超链接,依赖于 urls 中的 name 参数# 不想要这个功能的话完全可以注释掉mymodels=serializers.HyperlinkedRelatedField(many=True,queryset=MyModel.objects.all(),view_name='model-detail')classMySerializer(serializers.ModelSerializer):options=MyCustField(max_length=1000,style={'base_template':'textarea.html'},)classMeta:model=MyModelfields=('id','owner','field','options')read_only_fields=('owner',)# 指定只读的 fielddefcreate(self,validated_data):"""响应 POST 请求"""# 自动为用户提交的 model 添加 ownervalidated_data['owner']=self.context['request'].userreturnMyModel.objects.create(**validated_data)defupdate(self,instance,validated_data):"""响应 PUT 请求"""instance.field=validated_data.get('field',instance.field)instance.save()returninstance

ViewSet

定义好了 Serializers,就可以开始写 viewset 了

其实 viewset 反而是最简单的部分,rest_framework 原生提供了四种 ViewSet

ViewSet

GenericViewSet

继承于GenericAPIView

ModelViewSet

自身提供了六种方法

list

create

retrieve

update

partial_update

destroy

ReadOnlyModelViewSet

我比较喜欢用ModelViewSet,然后再用 Premissions 来管理权限

# myproject/myapp/views.py#! /usr/bin/env python# -*- coding: utf-8from__future__importunicode_literals,absolute_importfromdjango.contrib.auth.modelsimportUserfromrest_frameworkimportpermissions,viewsets,renderersfromrest_framework.decoratorsimport(permission_classes,detail_route)fromrest_framework.responseimportResponsefrom.serializersimportMySerializer,UserSerializerfrom.modelsimportMyModelclassUserViewSet(viewsets.ModelViewSet):queryset=User.objects.all()serializer_class=UserSerializer# 指定权限,下面马上讲到permission_classes=(permissions.IsAuthenticated,)classModelViewSet(viewsets.ModelViewSet):queryset=MyModel.objects.all()serializer_class=MySerializerpermission_classes=(permissions.IsAuthenticatedOrReadOnly,)@detail_route(renderer_classes=[renderers.StaticHTMLRenderer])defplaintext(self,request,*args,**kwargs):"""自定义 Api 方法"""model=self.get_object()returnResponse(repr(model))

我在 ModelViewSet 中自定义了方法 plaintext,rest_framework 中对于自定义的 viewset 方法提供了两种装饰器

list_route

detail_route

区别就是list_route的参数不包含pk(对应 list),而detail_route包含pk(对应 retrieve)

看一段代码就懂了

@list_route(methods=['post','delete'])defcustom_handler(self,request):pass@detail_route(methods=['get'])defcustom_handler(self,request,pk=None):pass

Filters

前面根据 serializers 和 viewset 我们已经可以很好的提供数据接口和展示了。但是有时候我们需要通过 url参数 来对数据进行一些排序或过滤的操作,为此,rest-framwork 提供了 filters 来满足这一需求。

全局filter

可以在 settings 里指定应用到全局的 filter:

REST_FRAMEWORK={'DEFAULT_FILTER_BACKENDS':('rest_framework.filters.DjangoFilterBackend',)}

viewset 的 filter

也可以为 viewset 分别指定 filter,方法就是在定义 viewset 的时候定义一个名为filter_backend的类变量:

classUserListView(generics.ListAPIView):queryset=User.objects.all()serializer=UserSerializerfilter_backends=(filters.DjangoFilterBackend,)

默认的 filter

rest-framework 提供了几个原生的 filter:

SearchFilter

filter_backends=(filters.SearchFilter,)search_fields=('username','email')# 指定搜索的域

请求http://example.com/api/users?search=russell。

OrderingFilter

filter_backends=(filters.OrderingFilter,)ordering_fields=('username','email')

请求http://example.com/api/users?ordering=account,-username。

自定义 filter

自定义 filter 非常简单,只需要定义filter_queryset(self, request, queryset, view)方法,并返回一个 queryset 即可。

直接贴一个我写的例子:

classNodenameFilter(filters.BaseFilterBackend):"""根据 nodename 来删选[nodename]: NeiWang"""deffilter_queryset(self,request,queryset,view):nodename=request.QUERY_PARAMS.get('nodename')ifnodename:returnqueryset.filter(nodename=nodename)else:returnqueryset

如果参数匹配有误,想要抛出异常的话,也可以自定义 APIError,举个例子:

fromrest_framework.exceptionsimportAPIExceptionclassFilterError(APIException):status_code=406default_detail='Query arguments error!'

然后在 viewset 里直接抛出raise FilterError即可。

Premissions

顾名思义就是权限管理,用来给 ViewSet 设置权限,使用 premissions 可以方便的设置不同级别的权限:

全局权限控制

ViewSet 的权限控制

Method 的权限

Object 的权限

被 premission 拦截的请求会有如下的返回结果:

当用户已登录,但是被 premissions 限制,会返回HTTP 403 Forbidden

当用户未登录,被 premissions 限制会返回HTTP 401 Unauthorized

默认的权限

rest_framework 中提供了七种权限

AllowAny# 无限制

IsAuthenticated# 登陆用户

IsAdminUser# Admin 用户

IsAuthenticatedOrReadOnly# 非登录用户只读

DjangoModelPermissions# 以下都是根据 Django 的 ModelPremissions

DjangoModelPermissionsOrAnonReadOnly

DjangoObjectPermissions

全局权限控制

在 settings.py 中可以设置全局默认权限

# settings.pyREST_FRAMEWORK={'DEFAULT_PERMISSION_CLASSES':('rest_framework.permissions.AllowAny',),}

ViewSet 的权限

可以设置permission_classes的类属性来给 viewset 设定权限,restframework 会检查元组内的每一个 premission,必须要全部通过才行。

classUserViewSet(viewsets.ReadOnlyModelViewSet):queryset=User.objects.all()serializer_class=UserSerializer# 设置权限,是一个元组permission_classes=(permissions.IsAuthenticated,)

自定义权限

Premissions 可以非常方便的定制,比如我就自己写了一个只允许 owner 编辑的权限

# myproject/myapp/premissions.py#! /usr/bin/env python# -*- coding: utf-8from__future__importunicode_literals,absolute_importfromrest_frameworkimportpermissionsclassIsOwnerOrReadOnly(permissions.BasePermission):defhas_permission(self,request,view):"""针对每一次请求的权限检查"""ifrequest.methodinpermissions.SAFE_METHODS:returnTruedefhas_object_permission(self,request,view,obj):"""针对数据库条目的权限检查,返回 True 表示允许"""# 允许访问只读方法ifrequest.methodinpermissions.SAFE_METHODS:returnTrue# 非安全方法需要检查用户是否是 ownerreturnobj.owner==request.user

urls & routers

# myproject/myapp/urls.py#! /usr/bin/env python# -*- coding: utf-8from__future__importunicode_literals,absolute_importfromdjango.conf.urlsimporturl,patterns,includefromrest_framework.routersimportDefaultRouterfrom.importviews# as_view 方法生成 view# 可以非常方便的指定 `{Http Method: View Method}`user_detail=views.UserViewSet.as_view({'get':'retrieve'})user_list=views.UserViewSet.as_view({'get':'list','post':'create'})# plaintext 是我的自定义方法,也可以非常方便的指定modal_plain=views.ModelViewSet.as_view({'get':'plaintext'})model_detail=views.ModelViewSet.as_view({'get':'retrieve','post':'create'})model_list=views.ModelViewSet.as_view({'get':'list','post':'create'})# router 的作用就是自动生成 Api Root 页面router=DefaultRouter()router.register(r'models',views.ModelViewSet)router.register(r'users',views.UserViewSet)# 不要忘了把 views 注册到 urls 中urlpatterns=patterns('',url(r'^',include(router.urls)),# Api Rooturl(r'^api-auth/',include('rest_framework.urls',namespace='rest_framework')),url(r'^models/(?P[0-9]+)/$',model_detail,name='model-detail'),url(r'^models/(?P[0-9]+)/plain/$',modal_plain,name='model-plain'),url(r'^models/$',model_list,name='model-list'),url(r'^users/$',user_list,name='user-list'),url(r'^users/(?P[0-9]+)/$',user_detail,name='user-detail'),)

时间仓促,就介绍这些,以后有空再介绍一下在 Django 用 JWT 作为身份凭证。下面是一些效果图

Api Root

利用 Django REST framework 编写 RESTful API_第3张图片

Users

利用 Django REST framework 编写 RESTful API_第4张图片

你可能感兴趣的:(利用 Django REST framework 编写 RESTful API)