Django Tastypie的问题

背景依旧是服务升级,从django1.6 python2.7 升级到 django3.2 python3.8

首先,先谈谈我对Tastypie关于api用法的整体认识。这东西是干啥的?在我看来,就是操作数据库的,进行增删改查,原本django是通过,建立模型和数据库进行关联的,对数据库的操作,也是直接通过操作模型来实现的,但是现在就可以通过Tastypie,对模型的资源做一个整合,就等于说,我们现在只需要操作Tastypie,然后Tastypie去操作模型,模型再关联数据库,通过这样的一种套路来进行数据库的增删改查。这东西,说它方便吧,到也方便,说不方便吧,也是真的不方便,怎么说呢,它的具体用法就是通过路由进行url访问,通过使用 post,delete,put, get,请求来分别实现对数据库的增删改查,说它不方便,是因为,通过路由的方式,我们往往是使用视图函数,来进行相关的操作,视图函数的方式,除了可以进行对数据库的增删改查操作,还可以做其他的许多事情,更加的灵活和便利。那么说它方便,那也是真的方便,这东西,往往不需要写很多的sql语句,只需要在url中添加相关的参数即可,其他的完全自动化,不用操心,然后,它本身的功能,也很强大,支持各种自定义操作,完全可以实现定制,所以说,如果只是要对数据库进行相关的操作的话,这个东西,就蛮不错的。

扯点概念性的东西

Tastypie 是一个基于Python在Django平台上用来创建RESTFul API 框架, 在创建RESTful API 方面提供了强大的功能, 同时在使用上可以很方便地利用Django 自身的Model作为数据源, 也可以很方便地使用非ORM数据源来提供API数据.

除了提供基本的数据功能之外, Tastypie 还提供了 登录验证, 权限控制, 数据验证, 数据缓存, 请求控制, 数据分页, 数据序列与反序列化等功能选项, 来增加API对数据源的各个方面的控制, 以方便我们在数据访问的不同阶段进行控制.

Tastypie 的主要框架架构组成为Api+Resource, Api是一个包含多个Resource集合的对象, Resource通过注册到Api对象中, 然后通过Api对象来进行统一管理. Resource 是一个资源管理实例, 在其中提供真正的资源数据管理, 上述的相关数据源控制的功能特性都是通过选项配置到Resource中来实现的, 也正是这种方式使得功能特性的实现与具体的Resource可以隔离进行处理, 做到逻辑上的解耦.

Tastypie 处理 RESTFul API 基本流程根据请求方式的不同分为三种: 用户读取数据, 用户生成或更新数据, 用户删除数据.

更多相关内容,可以看看这个帖子:

Django Tastypie 知识总结_theo_Liang的博客-CSDN博客

然后,直接聊聊它的使用。

首先,从大的功能上讲,这里分为2部分,一个是 tastypie.api 体系,一个是 tastypie.resources 体系,前者主要是用来统一管理后者的,后者就是真正的资源调度了。两个组合到一起就是一个整体,来实现Tastypie的相关功能,api更像是一个入口,一个引路人,接引者,而 resource 就是真正干活的,实现资源操作的,完成增删改查具体工作的worker。

接下来看下api的使用:

# 这里是url.py

from tastypie.api import Api
from . import api


api_v1 = Api(api_name='v1')

api_v1.register(api.CheckResource())
api_v1.register(api.ServiceResource())


urlpatterns = [
    url(r'^api/', include(api_v1.urls)),
]
# 这里是 api.py

from tastypie import fields
from tastypie.authentication import Authentication
from tastypie.authorization import Authorization
from tastypie.bundle import Bundle
from tastypie.resources import ModelResource, ALL

from . import models


class META_DEFAULTS:
    authentication = ApiKeyAuthentication()
    authorization = StaffUserAuthorization()
    always_return_data = True


class CheckResource(ModelResource):
    '''
    Endpoint wrapping the :class:`Check` model.
    '''

    class Meta(META_DEFAULTS):
        authentication = Authentication()
        queryset = models.Check.objects.all()
        filtering = dict(name=ALL)


class ServiceResource(ModelResource):
    '''
    Endpoint wrapping the :class:`Service` model.
    '''

    checks = fields.ToManyField(CheckResource, 'checks', full=True)

    class Meta(META_DEFAULTS):
        queryset = models.Service.objects.all()
        filtering = dict(id=ALL)

这两段代码,放到一起,就是一个完整的 tastypie 模块的使用例子。

先聊 url.py 这里的 tastypie 部分。

这里等于说是做了一个Api实例,取名为 v1,他将所有的 resource 进行了一个注册,就等于将所有的resource打了一个大的包,所以要访问resource的资源就需要先访问这个 Api,那么如何访问这个Api呢?这里这个Api在实例化的时候,传入的的参数 api_name 就显得尤为重要了,因为这个 api_name 就成了访问 resource 资源的url的路径的一部分,举个例子,按照当下代码的套路,我如果要访问 CheckResource 的资源的话,url就应该为:http://127.0.0.1:8000/api/v1/check/1,最后的1是指要访问的check表中的id为1的数据,这里注意,访问的路由上就必须有v1,且v1的位置在resource之前。有其他疑问先放一放,往后看。

ok,这是一种搞路由的方式,直接打包,然后放到路由里,给个 xxx.urls,就完事了,后面真正访问路由的时候,它会自动匹配相应的resource的。

除了这种打包的方式处理路由以外,还有一种不打包,直接用的,如下:

# 这是api.py

from tastypie.resources import ModelResource

from .models import Expense

class ExpenseResource(ModelResource):

    class Meta:
        queryset = Expense.objects.all()
        resource_name = 'expense'
# 这是url.py

from expenses.api import ExpenseResource

expense_resource = ExpenseResource()

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^api/', include(expense_resource.urls)),
)

这里,就没有进行Api的打包处理,直接将resource导入到url.py中,然后给与其实例化,完事后,同样添加到路由中 xxx.urls 完事。

这两种方法都行,第一种就是能在url这块的构建上方便一些,就写一个就行,xxx.urls,就包含所有的resource了,但是代价就是要添加一层路由,就是那个 api_name 参数,同时得完成register的动作,第二种方法,就是,二话不说,给所有resource实例化一波,然后就得在url这里全部写一遍,回头就可能写很多重复的路由,感觉可能也会比较怪怪的。比如这样:

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^api/', include(expense_resource.urls)),
    url(r'^api/', include(xxx.urls)),
    url(r'^api/', include(aaaa.urls)),
    url(r'^api/', include(bbb.urls)),
    url(r'^api/', include(cccc.urls)),
)

当然,这样的方法在访问时,自然就不牵扯 api_name 这个参数了,也就不牵扯http请求的url需要额外添加一层路由的问题了。

总之,两个方法都行吧。

ok,接下来,一起看看 tastypie 的 resource 这块。

这块不知道有没有人注意到,我在举第二个例子的时候,关于 api.py 文件的例子中,有这么个参数:resource_name,这个参数吧,讲道理,最好带着,实在不带,也行,因为每个api.py 中的我们自己定义的resource,名称都是由 nameResource组成的,如果这里没有给resource_name,那么,就会默认resource_name,为自定义resource类的类名的前半部分,同时是按小写字母处理的。举个例子,class CheckResource(ModelResource) ,上述代码中,有个这个类名的,在访问它的时候,url中用的是小写的check,http://127.0.0.1:8000/api/v1/check/1。所以,讲道理,最好带着resource_name,这个参数,多写一行代码又不会累死,尽量规范一点吧。对了,到现在基本上,大伙应该能猜出这个参数是干啥的吧,就是url中的一部分,不知道大伙有没有注意到,对于resource对象,基本都是直接以 xxx.urls 的形式扔到url中去了,他们的前半截还都一样都叫api,那么如何区分 http://127.0.0.1:8000/api/vi/check/1,http://127.0.0.1:8000/api/vi/service/1,谁到底访问的是谁呢?他们的区分,就是通过resource_name这个参数决定的,因为每个自定义的resource他们的名称肯定都是不一样,每个自定义的resource,都会关联不同的django model,所以,当url中的相同的部分走到尽头后,需要区分的时候,就看resource_name了,给哪个resource_name,就访问相应的自定义resource,所以,从某种角度说,resource_name就是自定义resource的对外路由名称。

我们先看看它的结构:

from tastypie.resources import ModelResource
from .models import Expense


class ExpenseResource(ModelResource):

    class Meta:
        queryset = Expense.objects.all()
        resource_name = 'expense'

每个自定义的resource,都会继承 tastypie.resources中一些Resource类,当然这里我们用的都是ModelResource,其他的目前是真没啥研究。然后它的命名,都是 nameResource,各位请注意,这里最好别搞创新,就按人家的格式来。每个 nameResource 类里都会由 class Meta,这个东西是必不可少的,一定得有。同时 class Meta 下面的参数,名称都是固定的,不能搞发明创造,给错了,直接完蛋,代码肯定就跑不通了。

queryset = Expense.objects.all()     这是标配,用来查询数据的

from tastypie.resources import ModelResource
from .models import Expense


class ExpenseResource(ModelResource):

    class Meta:
        queryset = Expense.objects.all()
        resource_name = 'expense'
        fields = ['description']

fields = ['description']         只显示哪些字段,就是对查询的结果进行过滤,将过滤后的结果返回

看一个 url :http://localhost:8000/api/expense/?amount__gt=150&description__icontains=pizza&format=json

这里就带有了条件查询

from tastypie.resources import ModelResource
from .models import Expense


class ExpenseResource(ModelResource):

    class Meta:
        queryset = Expense.objects.all()
        resource_name = 'expense'
        fields = ['description', 'amount']
        filtering = {
            'amount': ['gt'],
            'description': ['icontains']
        }

filtering = {
            'amount': ['gt']                   哪个字段,支持哪些过滤条件
           'description': ['icontains']        

     }

from tastypie.resources import ModelResource, ALL

class ServiceResource(ModelResource):
    '''
    Endpoint wrapping the :class:`Service` model.
    '''

    checks = fields.ToManyField(CheckResource, 'checks', full=True)

    class Meta(META_DEFAULTS):
        queryset = models.Service.objects.all()
        filtering = dict(id=ALL)

这里就不得不提另一种写法了:ALL

ALL: Enable all basic ORM filters but do not allow filtering across relationships.

这就等于一把将常规的过滤手段都给允许了。

from tastypie.authorization import Authorization


class ExpenseResource(ModelResource):

    class Meta:
        queryset = Expense.objects.all()
        resource_name = 'expense'
        fields = ['description', 'amount']
        filtering = {
            'amount': ['gt'],
            'description': ['icontains']
        }
        authorization = Authorization()

authorization = Authorization()           authorization这个参数本身是用来验证用户可以对资源做什么, 主要回答”用户是否授予了权限来执行该动作”, 其中涉及到以下权限的检 查:Create/Read/Update/Delete,它有三个选项可以绑定,

  • Authorization: 没有任何权限检查
  • ReadOnlyAuthorization: 仅允许用户读取数据.
  • DjangoAuthorization: 最高级的权限验证形式, 通过django.contrib.auth.models.Permission 来检查用户授予的权限.

通常如果不提供这个参数,默认给的是 ReadOnlyAuthorization(),所以之前的url都是在发get请求,都是在查数据。

按照上面的示例更改之后,就可以执行其他的操作了。比如:

post_url = 'http://localhost:8000/api/expense/'
post_data = {'description': 'Bought first Disworld book', 'amount': 399}
headers = {'Content-type': 'application/json'}
import requests
import json
r = requests.post(post_url, json.dumps(post_data), headers=headers)
>>> print(r.status_code)
201
# 如果给成 *r,就可以看到所有内容
>>> print(*r)
{
    "meta":{
        "limit":20,
        "next":null,
        "offset":0,
        "previous":null,
        "total_count":2
    },
    "objects":[
        {
            "amount":100,
            "description":"Ate pizza",
            "id":1,
            "resource_uri":"/api/expense/1/"
        },
        {
            "amount":200,
            "description":"Went to Cinema",
            "id":2,
            "resource_uri":"/api/expense/2/"
        }
    ]
}

 关于api token的创建有2中模式:

from django.contrib.auth.models import User
from tastypie.models import ApiKey

u = User.objects.create_user(username='sheryl', password='abc', email='sheryl@abc.com')

ApiKey.objects.create(key='1a23', user=u)
from tastypie.authentication import ApiKeyAuthentication

class ExpenseResource(ModelResource):

    class Meta:
        queryset = Expense.objects.all()
        resource_name = 'expense'
        fields = ['description', 'amount']
        filtering = {
            'amount': ['gt'],
            'description': ['icontains']
        }
 
        authentication = ApiKeyAuthentication()

这样以来,就会对url中传入的参数进行api_key的校验工作:

http://localhost:8000/api/expense/?format=json&username=sheryl&api_key=1a23

如果传错了,就会报401,即没有权限访问。

authentication = ApiKeyAuthentication()                 所以这个参数就是用来搞api_key检验的

再看一个示例:

from tastypie.authorization import Authorization


class ExpenseAuthorization(Authorization):

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


class ExpenseResource(ModelResource):

    class Meta:
        queryset = Expense.objects.all()
        resource_name = 'expense'
        fields = ['description', 'amount']
        filtering = {
            'amount': ['gt'],
            'description': ['icontains']
        }
        authorization = ExpenseAuthorization()
        authentication = ApiKeyAuthentication()

先做一个说明:

一个 Authorization 兼容的类实现了以下的方法:

- read_list(self, object_list, bundle)
- create_list(self, object_list, bundle)
- update_list(self, object_list, bundle)
- delete_list(self, object_list, bundle)

- read_detail(self, object_list, bundle)
- create_detail(self, object_list, bundle)
- update_detail(self, object_list, bundle)
- delte_detail(self, object_list, bundle)

上面的所有方法都接收 object_list 和 bundle 两个参数, 其中 object_list 是经过请求过滤以及其他限制条件处理之后得到的数据集合,简单来说,就是数据表中查询出来的数据,所有查询的数据都是存放在一个list中,其实这里也可以理解为存放在一个Queryset对象中,bundle 是一个用 request 填充的 Bundle 对象, 一般我们会经常用到 bundle.request.user 对象.

上述 Authorization 的方法基本上分为了两大类,一种是 xxx_list 类型,一种是 xxx_detail 类型,xxx_list 类型的方法是在处理,查出来的数据对象,哪些是可操作的,哪些不可操作,简单的来说,就是允许用户重写这些方法,对查询出来的数据做一些过滤性质的操作,然后将过滤后的结果返回,这样的做法会显得更加的自由。另一种 xxx_detail 类型的方法,主要是用来限定是否有相关权限的,它们的返回值就是 True or False , True就是有权限进行相关操作,False 就是没权限进行相关操作,回头就直接返回401了,所以,这里往往可以重写,以添加相关权限的验证。

ok,再回到上述的示例,那就不难理解了,最初我们给定的 authorization 值为 Authorization() 就是所有查询到的内容均可见,所有权限不做校验,均可使用。那么当上述示例中,我们继承Authorization()类,重写了read_list方法,就意味着,当触发get请求进行相关内容的查询时,read_list会对查询的结果进行过滤,只保留user为当前登录用户的user的数据,也就是说,谁发起的get查询请求,那就只能看到跟自己相关的数据,其他人的数据就看不到了。

再看一个示例:

from tastypie.authentication import Authentication
from tastypie.authorization import Authorization


class StatusResource(ModelResource):
    '''
    Endpoint wrapping the :class:`Status` model.
    '''

    check_new = fields.ToOneField(module_name_for('CheckResource'), 'check_new')

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

    class Meta(META_DEFAULTS):
        queryset = models.Status.objects.all()
        allowed_methods = ['get', 'post']
        limit = 20
        authentication = Authentication()
        authorization = Authorization()

先看看这个方法,def hydrate(self, bundle),这个方法是ModelResource自带的一个方法,这个方法在post和put的过程中会被调用,他的作用是什么呢?就是将bundle对象中的一些东西进行绑定,比如,拿上述示例来说,假如有这么一个url:

post_data = {'description': 'Paid for iDoneThis', 'amount': 700}
r = requests.post("http://localhost:8000/api/expense/?username=sheryl&api_key=1a23", data=json.dumps(post_data), headers=headers)

他在存储数据的时候,是不会将这条数据和user本身挂钩的,也就是说,即便是数据存储成功了,但是是谁去存储的这条数据,不知道,因为在存数据的时候,没有相关信息,那么如果按照上述的示例重写了def hydrate(self, bundle)方法,就会将即将存储的数据,和user关联起来,最后再查这是谁存的数据,就能知道了。这是因为bundle.obj是一个即将被保存进入数据库的Expense实例。

前面说了参数 authorization ,它主要是搞权限的,比如增删改查,你能做哪些不能做哪些,比如查询出来的数据,你可以操作哪些,不能操作哪些。上述示例中,还有一个参数:authentication,它是用来搞认证的,比如刚才看到的 authentication = ApiKeyAuthentication(),就是做 api_key 认证的,现在具体说说这个authentication 参数。

首先,他的主要作用就是,做用户认证配置的。

Tastypie 自带了以下的Authentication类可以使用:

  • Authentication: 没有任何验证的类, 主要用来只读的API
  • BasicAuthentication: 使用 HTTP Basic Auth 来检查一个用户的登录凭证.
  • ApiKeyAuthentication: 这个是一个敏感数据验证的备用选项, 可以用来验证一个用户名和机器码. 可以在header 的 Authorization 中赋值或者使用 username/api_key 来作为POST或GET的请求参数.

所以,上述示例中,authentication = Authentication() 就是不做认证的意思。

对了上述用例中有个这个东西,不知道有人注意到了没:

check_new = fields.ToOneField(module_name_for('CheckResource'), 'check_new')

再看一下:

#: Returns the fully qualified name of a class from this module.
module_name_for = lambda class_name: '.'.join([__name__, class_name])

class StatusResource(ModelResource):
    '''
    Endpoint wrapping the :class:`Status` model.
    '''

    check_new = fields.ToOneField(module_name_for('CheckResource'), 'check_new')

    class Meta(META_DEFAULTS):
        queryset = models.Status.objects.all()
        allowed_methods = ['get', 'post']
        limit = 20
        authentication = Authentication()
        authorization = Authorization()

首先,这里有和check_new,注意了,这里出现的每一个变量,都必须是当下对应的model表里所出现的字段的名称,比如:

 然后,这里大多数,都会给一些外键对应的字段,或者 onetoone or manytomany 对应的字段,其他的标准字段,是不需要写到这的,因为普通字段,都会正常显示的。那么这里做这种设定是为了干啥呢?这就是由于会有关系表的存在,让查询到的每个对象,在最终的返回结果里,多显示一些字段,拿当下的示例来看,就会多显示一个check_new字段,这样就相当于把每个对象本身的内容,以及和它相关的关联表的关联数据一起显示出来。这里还有个点需要注意:

举个详细的具体的例子:

# 这是models.py

#coding:utf-8
from django.db import models
 

class Author(models.Model):
    author_name = models.CharField(max_length=50)
 
class Book(models.Model):
    book_name = models.CharField(max_length=50)
    authors = models.ManyToManyField(Author)


# 这是 api.py

#coding:utf-8
from .models import Author, Book
from tastypie.resources import ModelResource
from tastypie import fields
 
class AuthorResource(ModelResource):
    class Meta:
        resource_name = 'author'
        queryset = Author.objects.all()
        allowed_methods = ['get']
        
class BookResource(ModelResource):
    authors = fields.ToManyField(AuthorResource, 'authors')
 
    class Meta:
        resource_name = 'book'
        queryset = Book.objects.all()
        allowed_methods = ['get']
# 然后,如果去发个get请求,拿到的结果就是这样的

[
    {
        'id':1,
        'authors':['/api/v1/author/1','/api/v1/author/2'],
        'book_name':'test_book1'
    },
    {
        'id':2,
        'authors':['/api/v1/author/2'],
        'book_name':'test_book2'
    }
]
# 这里如果添加两个参数 full=True,null=True 之后,看下结果
 
class BookResource(ModelResource):
    authors = fields.ToManyField(AuthorResource, 'authors', full=True,null=True)
 
    class Meta:
        resource_name = 'book'
        queryset = Book.objects.all()
        allowed_methods = ['get']
# 就获得了关联对象的完整的信息

[
    {
        'id':1,
        'authors':[{'id':1, 'author_name':'Bill'},{'id':2, 'author_name':'Lily'}],
        'book_name':'test_book1'
    },
    {
        'id':2,
        'authors':[{'id':1, 'author_name':'Bill'}],
        'book_name':'test_book2'
    }
]

full参数是设置是否展开对应全部信息。有时我们不需要在获取列表是显示全部,只需要在获取明细时显示全部,可以设置full_detail=True;反之,若要只在列表数据显示全部,可以设置full_list=True。ok,再说说,null = True,意思就是说,返回值可以为Null。

# BookResource设置关系没问题了,但AuthorResource没有设置外键关联到BookResource。若 
# AuthorResource也使用同样的方式设置外键,会有解析错误的问题。因为解析到AuthorResource时,还没有 
# 加入BookResource。面对这种情况,可以采用如下的设置方式:
# 通常可以直接写成 “BookResource” 这样,这种策略在 model.py 中很常见


class AuthorResource(ModelResource):
    book_set = fields.ToManyField('path.to.api.resources.BookResource', 'book_set')
    
    class Meta:
        resource_name = 'author'
        queryset = Author.objects.all()
        allowed_methods = ['get']

ok,再看一个发请求问题:

# 这是models.py


from django.db import models

class Check(models.Model):
    '''
    The check aggregate statuses about something we care.

    On each update the check refreshes it's current status, so we do not have
    to make heavy queries for it.
    '''

    name = models.CharField(
        help_text='The name of the check.',
        max_length=128,
        unique=True)

    description = models.TextField(
        help_text='Description for the check.',
        blank=True)


class Status(models.Model):
    '''
    The status is a single report to a bound check.
    '''

    value = models.SmallIntegerField(
        help_text='Numeric value of the status.',
        validators=[
            MinValueValidator(min(VALUE_CODES)),
            MaxValueValidator(max(VALUE_CODES))
        ],
        choices=VALUE_CHOICES)

    
    check_new = models.ForeignKey('Check', db_column='check',
        help_text='The check this status reports to.',
        related_name='status_history',
        on_delete=models.CASCADE)
# 这是api.py


class StatusResource(ModelResource):
    '''
    Endpoint wrapping the :class:`Status` model.
    '''

    # Tastypie does not follow relations, so we have to specify them explicitly.

    check_new = fields.ToOneField(module_name_for('CheckResource'), 'check_new')


    class Meta(META_DEFAULTS):
        queryset = models.Status.objects.all()
        allowed_methods = ['get', 'post']
        limit = 20
        authentication = Authentication()
        authorization = Authorization()

那么由于这俩属于外键关联表,那应该如何发这个post请求呢?现在需要给Status表创建一条数据,应该咋弄?

# 常规的创建,应该是这样的

check_1 = models.Check.objects.create(name='foo', interval=1)
models.Status.objects.create(value=-1, check_new=check_1)

但是现在是用post请求来创建:

# 先创建一条check的数据
Check.objects.create(name='foo', interval=600, id=1)

post_url = 'http://localhost:8000/api/status'
post_data = {'value': 0, 'check_new': '/api/check/1'}
headers = {'Content-type': 'application/json'}
import requests
import json
r = requests.post(post_url, json.dumps(post_data), headers=headers)

重点看,这时候传入外键字段的时候,是没办法直接传一个对象的,所以,传入的就是一个url,这个url则,对应具体的确定的一个外键字段对应的对象,上述代码的意思就是,创建一条status数据,其中check_new字段关联的对象是,从check表里取出来id=1的那条数据对象,这里参数:'/api/check/1',中的1,就是id=1的意思。

上面说了,关于api token的创建有2中模式,最后,再看一个api token的创建:

from django.contrib.auth.models import User

user = User.objects.create_user('test', 'test@example.com')

def perform_request(path, data={}, **options):
        client = Client(user=user)
        options.setdefault('content_type', 'application/json')
        options.setdefault('HTTP_AUTHORIZATION', 'apikey %s:%s' % (user.username, user.api_key.key))
        return client.post(path, data, **options)

系统创建的user会生成随机的api_key,同时这个api_key还会被自动的存入到tastypie.models.ApiKey中。

同时,这里这样定义之后,需要在认证模块那边进行处理。实现自定义认证。

自定义用户凭证验证器实际上很简单, 只需要重载 Authentication的两个方法(其中一个是可选的, 推荐定制, 在API的Caching 和 Throttle中会用到)

# 这个函数,返回结果决定,认证是否通过   
def is_authenticated(self, request, **kwargs):
    return True/False

# 这个函数,则是从request中返回,传入的用户名username
def get_identifier(self, request):
    return string_value

那么所以,只需要从request中取出username and api_key,然后,再通过 ApiKey.objects.get(user=username ,key=api_key)就可以验证是否认证成功了。只要is_authenticated 返回True就认证成功,返回false肯定就是失败。至于另外一个函数,直接取username返回就行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值