初步使用tastypie的体悟

  • 发布时间:2017年7月26日 11:32
  • 作者:杨仕航
  • 分类标签: Django
  • 阅读(10009)
  • 评论(1)

虽然是初次接触tastypie,不过前前后后我也仔细研读tastypie的帮助文档。并在实际开发项目中使用了1个多月,有所体悟,执笔记录。哦,正确来说是敲键盘记录。

tastypie是django的第三方库,可以轻松实现RESTful风格的API接口。

若你不知道什么是RESTful,可以先看看下面两篇文章。否则你可能不知道我在说什么。

1)理解restful架构

2)restful api指南


目前tastypie更新到0.14.0版。不过这个版本有个bug,会提示“AttributeError: class Meta has no attribute 'object_class'.”错误。建议使用0.13.3版

pip install django-tastypie==0.13.3


Resource(资源)是RESTful基本概念,tastypie提供两种Resource类:Resource和ModelResource。

由于RESTful一般都是针对数据操作,所以我建议尽可能使用ModelResource。

ModelResource是基于Django的Model快速创建相应的资源。我们可以很方便使用tastypie提供的方法等。如果用Resource类完全自己定义十分费劲。

使用ModelResource,我们一般只需要设置Meta。具体设置可参考tastypie的文档。

一般Meta需要设置如下信息:

#coding:utf-8
from tastypie.resources import ModelResource
from tastypie.authorization import Authorization
from django.contrib.auth.models import User

class UserResource(ModelResource):
    class Meta:
        resource_name = 'user'  # 名称
        queryset = User.objects.all()  # 数据集
        
        # 只显示哪些字段
        fields = ['username', 'email',]
        
        # 允许的请求方式
        list_allowed_methods = ['get', 'post']
        detail_allowed_methods = ['get', 'patch']
        
        # 权限
        authorization = Authorization()


大致作用已经写在注释中,具体详细用法查官方文档即可。你可能会问Authentication、validation为什么没写,因为这两个是我在使用tastypie过程中,用起来不是很流畅的东西。

尤其是我采用测试驱动的方式开发,这些体会比较明显。

为了更好让大家充分理解这些东西,讲这些之前先给大家说一下当get或post的时候,tastypie的执行中比较重要的方法。

get的时候,会先执行ModelResource的obj_get方法再执行dehydrate方法。你可以继承dehydrate方法输出一些自己想额外添加的东西。

def dehydrate(self, bundle):
    bundle.data['is_test'] = True
    return bundle


post的时候,会执行主要执行的是obj_create方法。执行obj_create方法时,会先执行hydrate方法处理我们提交的数据。

bundle的data属性可以得到我们post提交的数据。bundle的obj属性可以得到我们要创建的对象。

在执行hydrate时,bundle.obj为None。hydrate主要是去掉一些杂质,得到创建对应的object需要的数据。


hydrate执行完成之后,再进行validation验证。tastypie的validation验证可以使用django的Form表单验证。

这样简化了很多格式等常规验证。但问题来了,通常API接口如果请求有问题时,我们需要返回对应的错误码。该错误码可以让API接口的使用者知道相应的错误,方便API接口使用者进行调试。

如果用Form表单验证,返回一个errors错误集合。该集合会将多个错误信息写在一起。而且该errors错误信息不能自定义,会直接暴露一些敏感字段名和容易被推测一些执行逻辑。

若不使用Form表单进行验证。那么在hydrate验证和validation验证有什么区别。

所以,本人觉得validation有些鸡肋。


我们继续顺着这个流程走下去。validation执行之后,会得到一个bundle.obj。该object是对应queryset绑定的相关model实例化对象。该object尚未被保存。

接着继续执行obj_create方法,真正创建object。


这里还会衍生另外一个问题:谁可以post,甚至更细致谁可以get、post、patch、delete等请求。该问题就是权限问题。

tastypie也提供了一些基本权限验证。对于复杂的权限需求,我个人更喜欢自定权限类。 

#coding:utf-8
from tastypie.authorization import Authorization
from tastypie.exceptions import Unauthorized

class UserObjectsOnlyAuthorization(Authorization):
    def read_list(self, object_list, bundle):
        return object_list.filter(user=bundle.request.user)

    def read_detail(self, object_list, bundle):
        return bundle.obj.user == bundle.request.user

    def create_list(self, object_list, bundle):
        return object_list

    def create_detail(self, object_list, bundle):
        return bundle.obj.user == bundle.request.user

    def update_list(self, object_list, bundle):
        allowed = []

        # Since they may not all be saved, iterate over them.
        for obj in object_list:
            if obj.user == bundle.request.user:
                allowed.append(obj)
        return allowed

    def update_detail(self, object_list, bundle):
        return bundle.obj.user == bundle.request.user

    def delete_list(self, object_list, bundle):
        raise Unauthorized("Sorry, no deletes.")

    def delete_detail(self, object_list, bundle):
        raise Unauthorized("Sorry, no deletes.")


在自定义权限中,bundle.obj对应的model要有user字段。该字段可以根据具体情况修改。

如果你某个ModelResource使用自定义权限类,但又有一些特殊的权限判断,例如判断身份级别。该判断可以重写authorized_delete_detail、authorized_update_detail等方法。这些方法是在权限验证之前执行。


可能你会问,权限验证是针对登录的用户或者没登录的访客。那么如何使用tastypie登录?

这点我也研究了很久。tastypie文档中提到api_key这个东西。可能我学识还不到家,没研究明白如何使用api_key。

所以,继续采用Django提供的登录方式。

#coding:utf-8
from tastypie.resources import ModelResource
from tastypie.authorization import Authorization
from tastypie.exceptions import Unauthorized

from django.conf.urls import url
from tastypie.utils import trailing_slash
from django.contrib import auth

class UserResource(ModelResource):
    class Meta:
        resource_name = 'user'
        queryset = User.objects.all()

        list_allowed_methods = ['get']
        detail_allowed_methods = ['get']
        
        authorization = Authorization()

        # 或者使用fields直接选择要显示的字段
        fields = ['username', 'email']


    def prepend_urls(self):
        urlpatterns = [
            url(r'^(?P<resource_name>%s)/login%s' % (self._meta.resource_name, trailing_slash()),
                self.wrap_view('login'), 
                name="api_login"),
        ]
        return urlpatterns

    def login(self, request, **kwargs):
        # 只允许POST提交
        self.method_check(request, allowed=['post'])
        
        # 序列化提交的参数
        data = self.deserialize(request, 
            request.body, 
            format=request.META.get('CONTENT_TYPE', 'application/json'))
            
        # 获得用户名和密码加密的密文
        username = data.get('username', '')
        password = data.get('password', '')
        
        # 验证username是否是邮箱,实现可用邮箱登录
        users = User.objects.filter(email=username)
        if users.count():
            username = users[0].username

        # 登录
        data = {}
        user = auth.authenticate(username=username, password=password)
        if user:
            auth.login(request, user)
            return self.create_response(request, {'stats':'OK'})
        else:
            raise Unauthorized()


该处理方法是通过prepend_urls自定义一个api接口。通过该接口执行Django的登录方法。同样可以使用该方法创建登出的方法。

用户认证完整的功能还包括注册。注册实际上创建新的User,对应POST请求。这个大家可以试验一下。

初步使用大致体悟是这些。当然有关字段什么没讲,这些需要实践操作。更多体悟后面再说。有关tastypie欢迎多交流交流。

上一篇:tastypie中model之间的关系

下一篇:Excel数组公式入门

评论列表

pengwk

pengwk

😀

2018-06-13 15:00 回复

新的评论

清空