码迷,mamicode.com
首页 > 其他好文 > 详细

rest-framework框架的基本组件

时间:2018-04-10 21:50:16      阅读:656      评论:0      收藏:0      [点我收藏+]

标签:message   过滤   iss   linked   重新定义   exp   http   name   inview   

快速实例

Quickstart

大致步骤

技术分享图片
(1)创建表,数据迁移
(2)创建表序列化类BookSerializer
   class BookSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
             model=Book
             fields="__all__"
             
(3)创建视图类:
    class BookViewSet(viewsets.ModelViewSet):
          queryset = Book.objects.all()
          serializer_class = BookSerializer

(4) 设计url:
     router.register(r‘books‘, views.BookViewSet)
技术分享图片

序列化

创建一个序列化类

简单使用

开发我们的Web API的第一件事是为我们的Web API提供一种将代码片段实例序列化和反序列化为诸如json之类的表示形式的方式。我们可以通过声明与Django forms非常相似的序列化器(serializers)来实现。

models部分:

技术分享图片
from django.db import models

# Create your models here.


class Book(models.Model):
    title=models.CharField(max_length=32)
    price=models.IntegerField()
    pub_date=models.DateField()
    publish=models.ForeignKey("Publish")
    authors=models.ManyToManyField("Author")
    def __str__(self):
        return self.title

class Publish(models.Model):
    name=models.CharField(max_length=32)
    email=models.EmailField()
    def __str__(self):
        return self.name

class Author(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField()
    def __str__(self):
        return self.name
技术分享图片

views部分:

技术分享图片
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from django.shortcuts import HttpResponse
from django.core import serializers


from rest_framework import serializers

class BookSerializers(serializers.Serializer):
    title=serializers.CharField(max_length=32)
    price=serializers.IntegerField()
    pub_date=serializers.DateField()
    publish=serializers.CharField(source="publish.name")
    #authors=serializers.CharField(source="authors.all")
    authors=serializers.SerializerMethodField()
    def get_authors(self,obj):
        temp=[]
        for author in obj.authors.all():
            temp.append(author.name)
        return temp


class BookViewSet(APIView):

    def get(self,request,*args,**kwargs):
        book_list=Book.objects.all()
        # 序列化方式1:
        # from django.forms.models import model_to_dict
        # import json
        # data=[]
        # for obj in book_list:
        #     data.append(model_to_dict(obj))
        # print(data)
        # return HttpResponse("ok")

        # 序列化方式2:
        # data=serializers.serialize("json",book_list)
        # return HttpResponse(data)

        # 序列化方式3:
        bs=BookSerializers(book_list,many=True)  # 当传的是一个queryset时要加many=True,如果是一个单个对象则不用加
     return Response(bs.data)
技术分享图片

我们可以看到当我们拿到book_list后,有很多种方式来进行序列化,这里我们使用的是rest_framwork带的serializers来进行的序列化,在返回时我们也用到了rest_framwork的Response

还有一点值得注意的时,我们之前在写CBV时,继承的django.views中的View类,而这里我们是从rest_framwork.views中导入的APIView,我们来看看这个类具体做了什么

首先,在url中我们可以看到

技术分享图片
from django.conf.urls import url
from django.contrib import admin
from api import views

urlpatterns = [
    url(r‘^admin/‘, admin.site.urls),
    url(r‘^books/$‘, views.BookView.as_view()),
]
技术分享图片

这里我们还是使用的as_view这个方法,那么在APIView中这个方法是如何定义的呢

技术分享图片
@classmethod
    def as_view(cls, **initkwargs):
        """
        Store the original class on the view function.

        This allows us to discover information about the view when we do URL
        reverse lookups.  Used for breadcrumb generation.
        """
        if isinstance(getattr(cls, ‘queryset‘, None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    ‘Do not evaluate the `.queryset` attribute directly, ‘
                    ‘as the result will be cached and reused between requests. ‘
                    ‘Use `.all()` or call `.get_queryset()` instead.‘
                )
            cls.queryset._fetch_all = force_evaluation

        view = super(APIView, cls).as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view)
技术分享图片

可以看到这里其实是通过super方法执行的它父类的as_view方法,而它的父类正式django的View类,所以这里的view其实就是父类执行as_view方法获得的view函数,在url中得到这个函数后,会自动去执行它,而执行它其实就是在执行dispatch方法

技术分享图片
def view(request, *args, **kwargs):
    self = cls(**initkwargs)
    if hasattr(self, ‘get‘) and not hasattr(self, ‘head‘):
        self.head = self.get
    self.request = request
    self.args = args
    self.kwargs = kwargs
    return self.dispatch(request, *args, **kwargs)
技术分享图片

我们来看看APIView的dispatch方法都做了什么

技术分享图片
def dispatch(self, request, *args, **kwargs):
    """
    `.dispatch()` is pretty much the same as Django‘s regular dispatch,
    but with extra hooks for startup, finalize, and exception handling.
    """
    self.args = args
    self.kwargs = kwargs
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?
    try:
        self.initial(request, *args, **kwargs)
        # Get the appropriate handler method
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(),
                              self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        response = handler(request, *args, **kwargs)
    except Exception as exc:
        response = self.handle_exception(exc)
    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response
技术分享图片

这里的dispatch首先重新定义了一下request,request = self.initialize_request(request, *args, **kwargs),这个initialize_request方法的内容如下

技术分享图片
    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
技术分享图片

可以看到它其实就是返回了一个Request类的实例对象,那么这个类又做了什么呢

技术分享图片
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            ‘The `request` argument must be an instance of ‘
            ‘`django.http.HttpRequest`, not `{}.{}`.‘
            .format(request.__class__.__module__, request.__class__.__name__)
        )

        self._request = request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()
        self.negotiator = negotiator or self._default_negotiator()
        self.parser_context = parser_context
        self._data = Empty
        self._files = Empty
        self._full_data = Empty
        self._content_type = Empty
        self._stream = Empty

        if self.parser_context is None:
            self.parser_context = {}
        self.parser_context[‘request‘] = self
        self.parser_context[‘encoding‘] = request.encoding or settings.DEFAULT_CHARSET

        force_user = getattr(request, ‘_force_auth_user‘, None)
        force_token = getattr(request, ‘_force_auth_token‘, None)
        if force_user is not None or force_token is not None:
            forced_auth = ForcedAuthentication(force_user, force_token)
            self.authenticators = (forced_auth,)
技术分享图片

别的暂时先不看,我们可以看到它定义了一个self._request = request,所以我们以后如果想使用wsgi封装的request其实需要用request._request才行

在定义完新的request之后,dispatch又进行了一步初始化操作self.initial(request, *args, **kwargs),这个我们后面再说,然后进行的就和View类一样,判断请求的方式是否在self.http_method_names中,如果在就利用反射的方式取到,然后执行,但是这样要注意的是,这时后执行get,post等方法时传进去的request已经是重新定义过后的了

看完了源码的大体流程,我们再来看看序列化的类干了什么

技术分享图片
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.views import APIView


class BookSerializers(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()
    pub_date = serializers.DateField()
    publish = serializers.CharField(source="publish.id")
    # authors = serializers.CharField(source="authors.all")
    # 针对多对多
    authors = serializers.SerializerMethodField()
    def get_authors(self, obj):
        temp = []
        for author in obj.authors.all():
            temp.append(author.pk)
        return temp
技术分享图片

首先定义这个类其实和我们之前使用的form有点像,不过这里要注意一对多的字段,有个source属性,可以控制我们具体显示的内容,默认是显示这个对象,这里我们可以定义为对象的id等,而多对多的字段我们可以自定义一个方法

serializers.SerializerMethodField()是固定写法,而下面的函数名必须是get_字段名,函数的参数obj就是每一个book对象,这样我们通过这个类,在使用postman进行get请求时就能得到如下的数据

技术分享图片
[
    {
        "id": 1,
        "authors": [
            1,
            2
        ],
        "title": "python",
        "price": 123,
        "pub_date": "2018-04-08",
        "publish": 1
    },
    {
        "id": 2,
        "authors": [
            1
        ],
        "title": "go",
        "price": 222,
        "pub_date": "2018-04-08",
        "publish": 2
    },
    {
        "id": 3,
        "authors": [
            2
        ],
        "title": "java",
        "price": 222,
        "pub_date": "2018-04-08",
        "publish": 2
    }
]
技术分享图片

上面的写法跟普通的form类似,当然我们还学过ModelForm,这里也有类似的用法

技术分享图片
class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"

    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for author in obj.authors.all():
            temp.append(author.pk)
        return temp
技术分享图片

这里如果不单独定义多对多的字段和一对多的字段,那么默认显示的是字段的pk值

提交post请求

技术分享图片
  def post(self,request,*args,**kwargs):
       
        bs=BookSerializers(data=request.data,many=False)
        if bs.is_valid():
            # print(bs.validated_data)
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)
技术分享图片

post请求是用来添加数据的,这里的request经过了新的封装,我们可以从request.data中拿到数据,然后就像使用form表单一样使用serializers,实例化一个对象,通过is_valid方法验证数据,如果没问题直接save保存数据,有问题的话则返回错误信息,这里要注意,当数据保存后我们要将新添加的数据返回(Json格式)

各种情况下需要返回的内容

技术分享图片
全部信息
    users/ 
     
          ----查看所有数据
          get   users/    :返回所有的用户的json数据        
          ----提交数据    
         post  users/    :返回添加数据的json数据        
具体对象
     users/2
          ----查看
          get  users/2    :返回具体查看的用户的json数据
          ----删除
          delete users/2  :返回空文档
          
          ----编辑      
          put/patch   users/2:返回的编辑后的json数据包
技术分享图片

单条数据的get、put和delete请求

这里需要对单条数据进行操作,所以要有一个新的url和新的视图

技术分享图片
from django.conf.urls import url
from django.contrib import admin
from api import views

urlpatterns = [
    url(r‘^admin/‘, admin.site.urls),
    url(r‘^books/$‘, views.BookView.as_view(), name="book_list"),
    url(r‘^books/(\d+)/$‘, views.BookDetailView.as_view(), name="book_detail"),
]
技术分享图片

新的url对应的新视图

技术分享图片
# 针对Book某一个数据操作 查看,编辑,删除一本书
class BookDetailView(APIView):
    # 查看一本书
    def get(self, request, pk, *args, **kwargs):
        obj = Book.objects.filter(pk=pk).first()
        if obj:
            bs = BookSerializers(obj)
            return Response(bs.data)
        else:
            return Response()

    # 编辑一本书
    def put(self, request, pk, *args, **kwargs):
        obj = Book.objects.filter(pk=pk).first()
        bs = BookSerializers(data=request.data, instance=obj)
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return Response(bs.errors)

    # 删除一本书
    def delete(self, request, pk, *args, **kwargs):
        Book.objects.filter(pk=pk).delete()
        return Response()
技术分享图片

查看时跟我们之前的区别在于,之前是序列化的一个queryset,现在只是一个单个对象,然后将这个对象的序列化结果返回,这里多加了一步判断,如果这个对象不存在则返回空

编辑时,我们首先通过主键值取到这个书对象,然后像使用form组件一样,将更新的数据和这个对象传给我们的序列化类进行实例化,这里注意参数instance和form组件一样,然后通过is_valid验证数据是否正确,如果正确则save并返回这条记录,错误则返回错误信息

删除时,直接过滤出我们要删除的进行删除即可,返回空

超链接API:Hyperlinked

采用上面的内容,我们访问时,外键内容我们可以看到的是主键值,但是如果我们想看到一个链接url呢,那么就要采用下面的方法

技术分享图片
class BookSerializers(serializers.ModelSerializer):
      publish= serializers.HyperlinkedIdentityField(
                     view_name=‘publish_detail‘,
                     lookup_field="publish_id",
                     lookup_url_kwarg="pk")
      class Meta:
          model=Book
          fields="__all__"
          #depth=1
技术分享图片

这里我们重新定义了publish字段,view_name是url对应的别名,lookup_field则是url上对应的需要显示的内容,lookup_url_kwarg是url上的分组名称

urls部分:

urlpatterns = [
    url(r‘^books/$‘, views.BookViewSet.as_view(),name="book_list"),
    url(r‘^books/(?P<pk>\d+)$‘, views.BookDetailViewSet.as_view(),name="book_detail"),
    url(r‘^publishers/$‘, views.PublishViewSet.as_view(),name="publish_list"),
    url(r‘^publishers/(?P<pk>\d+)$‘, views.PublishDetailViewSet.as_view(),name="publish_detail"),
]

这样,我们访问时就能看到publish的结果是一个url链接

技术分享图片

视图

使用混合(mixins)

使用我们上面的方法,如果我们要再定义一个publish表的操作,则需要再写两个视图

技术分享图片
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from django.shortcuts import HttpResponse
from django.core import serializers


from rest_framework import serializers


class BookSerializers(serializers.ModelSerializer):
      class Meta:
          model=Book
          fields="__all__"
          #depth=1


class PublshSerializers(serializers.ModelSerializer):

      class Meta:
          model=Publish
          fields="__all__"
          depth=1


class BookViewSet(APIView):

    def get(self,request,*args,**kwargs):
        book_list=Book.objects.all()
        bs=BookSerializers(book_list,many=True,context={‘request‘: request})
        return Response(bs.data)


    def post(self,request,*args,**kwargs):
        print(request.data)
        bs=BookSerializers(data=request.data,many=False)
        if bs.is_valid():
            print(bs.validated_data)
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class BookDetailViewSet(APIView):

    def get(self,request,pk):
        book_obj=Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,context={‘request‘: request})
        return Response(bs.data)

    def put(self,request,pk):
        book_obj=Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,data=request.data,context={‘request‘: request})
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class PublishViewSet(APIView):

    def get(self,request,*args,**kwargs):
        publish_list=Publish.objects.all()
        bs=PublshSerializers(publish_list,many=True,context={‘request‘: request})
        return Response(bs.data)


    def post(self,request,*args,**kwargs):

        bs=PublshSerializers(data=request.data,many=False)
        if bs.is_valid():
            # print(bs.validated_data)
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class PublishDetailViewSet(APIView):

    def get(self,request,pk):

        publish_obj=Publish.objects.filter(pk=pk).first()
        bs=PublshSerializers(publish_obj,context={‘request‘: request})
        return Response(bs.data)

    def put(self,request,pk):
        publish_obj=Publish.objects.filter(pk=pk).first()
        bs=PublshSerializers(publish_obj,data=request.data,context={‘request‘: request})
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)
技术分享图片

可以方法,代码基本都是重复的,context={‘request‘: request}是使用超链接API时需要加的内容

通过使用mixin类编写视图:

技术分享图片
from rest_framework import mixins
from rest_framework import generics

class BookViewSet(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):

    queryset = Book.objects.all()
    serializer_class = BookSerializers

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)



class BookDetailViewSet(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)
技术分享图片

这里我们用到了多继承,当执行对应的方法时,其实会一步一步的从每一个继承的类中去找,这里我们以BookViewSet的get方法为例,当执行它时其实执行的是self.list(request, *args, **kwargs),这个list方法我们可以在它的第一个继承类mixins.ListModelMixin中找到

技术分享图片

这个方法的内容其实和我们自己写的get方法是类似的,先拿到queryset,再利用序列化类进行实例化,最后返回序列化的结果

其它的方法也都跟这里类似,执行一个方法时只要按照继承顺序一步一步的找就行了

通过这种方式,我们就可以不用自己写那么多代码,而是直接使用继承类的内容,但是如果要再写一个publish表还是要写很多重复的内容

使用通用的基于类的视图

通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py模块

技术分享图片
from rest_framework import mixins
from rest_framework import generics

class BookViewSet(generics.ListCreateAPIView):

    queryset = Book.objects.all()
    serializer_class = BookSerializers

class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers

class PublishViewSet(generics.ListCreateAPIView):

    queryset = Publish.objects.all()
    serializer_class = PublshSerializers

class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView):
    queryset = Publish.objects.all()
    serializer_class = PublshSerializers
技术分享图片

generics.ListCreateAPIView,我们可以点击进去看看这个类的内容

 技术分享图片

可以看到它已经帮我们把get和post方法都封装好了,其它的类也做了一样的事情,所以我们在写时,只需要继承这些类,然后在我们自己类中定义好queryset和serializer_class这两个参数即可,这样重复的代码就不用再写了

 再次优化

通过上面的方法我们已经将代码优化了很多,那么还能不能再进行优化呢,其实还可以将我们定义的两个类合并

from rest_framework.viewsets import  ModelViewSet

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializers

这里我们继承了一个新的类,可以看看这个类都有一些什么内容

技术分享图片

可以看到其实这个类就是继承了我们上面用到的各种类,这么用以后,为了加以区分,在url中我们需要多传一点参数

技术分享图片
views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"),
    url(r‘^books/(?P<pk>\d+)$‘, views.BookViewSet.as_view({
                ‘get‘: ‘retrieve‘,
                ‘put‘: ‘update‘,
                ‘patch‘: ‘partial_update‘,
                ‘delete‘: ‘destroy‘
            }),name="book_detail"),
技术分享图片

认证与权限组件

认证组件

局部视图认证

为了做认证,我们先要有用户相关的表,所以我们先添加表结构

技术分享图片
from django.db import models


# Create your models here.


class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    pub_date = models.DateField()
    publish = models.ForeignKey("Publish")
    authors = models.ManyToManyField("Author")

    def __str__(self):
        return self.title


class Publish(models.Model):
    name = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    def __str__(self):
        return self.name


class User(models.Model):
    user = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    user_type = models.IntegerField(choices=((1, "普通用户"), (2, "VIP"), (3, "SVIP")), default=1)


class UserToken(models.Model):
    user = models.OneToOneField("User")
    token = models.CharField(max_length=128)
技术分享图片

这里我们添加了两个表,一个用户表还有一个用户token表,token表中的token信息就是我们用来认证的字符串

表建好后,我们就要来创建login的逻辑了,这里的login我们只需要创建post请求的逻辑

技术分享图片
from django.http import JsonResponse


def get_random_str(user):
    import hashlib, time
    ctime = str(time.time())

    md5 = hashlib.md5(bytes(user, encoding="utf8"))
    md5.update(bytes(ctime, encoding="utf8"))

    return md5.hexdigest()


class LoginView(APIView):

    def post(self, request, *args, **kwargs):
        user = request.data.get("user")
        pwd = request.data.get("pwd")
        token = request.data.get("token")
        user = User.objects.filter(user=user, pwd=pwd).first()
        res = {"state_code": 200, "msg": None}
        if user:
            random_str = get_random_str(user.user)
            UserToken.objects.update_or_create(user=user, defaults={"token": random_str})
            res["msg"] = "success"
            res["token"] = random_str
        else:
            res["msg"] = "用户名或密码错误"
            res["state_code"] = 110
        return JsonResponse(res)
技术分享图片

当用户登录后我们先要判断用户名和密码是否正确,如果正确的话,我们通过md5生成一串随机字符串,然后将这个随机字符串添加到usertoken表中,这里要注意的是我们用到了update_or_create,当user=user的数据存在时就更新,否则就添加

如果登录不成功,则返回错误信息

用户登录完成后,我们就开始做认证了,首先我们需要创建一个认证类

技术分享图片
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication


class Authentication(BaseAuthentication):
    def authenticate(self, request):
        token = request._request.GET.get("token")
        user_token_obj = UserToken.objects.filter(token=token).first()
        if user_token_obj:
            return user_token_obj.user, token
        else:
            raise exceptions.AuthenticationFailed("token验证失败")
技术分享图片

类名随便取,但是类中必须有一个authenticate方法,在这个方法中做我们的逻辑判断,这里我们就从url上拿token值到数据库中搜索,如果有表示登录成功,这是需要返回一个元组,元组的两个值可以根据需要返回,如果不成功则要抛出一个异常

类定义完后我们就需要在我们的视图类中添加一个属性

class BookViewSet(ModelViewSet):
    authentication_classes = [Authentication]
    queryset = Book.objects.all()
    serializer_class = BookSerializers

这样,当用户访问Book表相关功能时就会进行认证了

全局视图认证组件

上面我们介绍了局部认证,那么如果想让全局都进行认证呢,只需要在settings配置中增加

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",]
}

字典中的内容为认证类的路径

源码分析

上面我们介绍了认证组件的用法,那么为什么这么用呢,这就要通过源码来进行分析了,首先当用户来访问时,url匹配到了我们先执行的是一个as_view方法,这里我们从BookViewSet类继承的ModelViewSet类开始一步步往上找

首先我们来看看这些类的继承顺序

技术分享图片

然后再一步步找

技术分享图片

ModelViewSet中没有as_view

 技术分享图片

GenericViewSet中也没有,generics.GenericAPIView中也没有,最后在generics.GenericAPIView的父类APIView中发现as_view方法,这个方法在上面我们已经提到了,他返回了一个view函数,当匹配到url后,会执行这个view函数并给它传一个request

技术分享图片

而执行这个view其实就是在self.dispatch方法,我们还是一步步找,最后还是在APIView找到了,这个方法我们上面说到了他重新封装了request,然后又进行了初始化操作

技术分享图片

现在我们来看看初始化操作self.initial方法做了什么

技术分享图片

上面的操作我们先不管,我们发现这里有一步关于认证的操作self.perform_authentication(request),这里到底做了什么呢

技术分享图片

我们发现这里只有一个request.user,我们猜想这是一个静态方法,这时我们就要去重新定义request的类中找这个方法了

技术分享图片

技术分享图片

技术分享图片

可以看到这里其实就是执行了self._authenticate()方法

技术分享图片

在这个方法中我们看到在for循环self.authenticators,那么这个self.authenticators是什么呢,我们注意到在Request类的初始化方法__init__中我们对它进行了定义

 技术分享图片

那么我们在实例化的过程中传了什么呢

技术分享图片

这里我们可以看到这里调用了self.get_authenticators()的方法

技术分享图片

这个方法其实就是放回了一个列表,这个列表解析式中我们发现self.authentication_classes就是我们在局部认证时加在我们的视图类中的内容,是一个列表中放着认证类

所以这里返回的列表中放着的其实就是我们的认证类对象

技术分享图片

知道了这个内容后,我们在上图所示的方法中其实就是在循环认证类对象的列表,然后调用对象的authenticate方法,所以我们在创建认证类时必须要有这个方法,而这个方法返回的是一个元组,在下面分别给self.user和self.auth赋值这个元组中的值

到这里我们就明白了为什么局部认证时要在视图类中设置authentication_classes,并且认证类中一定要有authenticate方法了,那么全局认证时又是为什么呢

我们发现,如果我们在视图类中没有定义authentication_classes属性,那么在找authentication_classes时,我们会在APIView中找到

技术分享图片

这里的api_settings是一个实例化的对象

技术分享图片

在这个类中都有什么内容呢

技术分享图片

首先我们发现,如果我们没有传值,这个defaults会有一个默认的内容DEFAULTS

技术分享图片

这就是我们不做任何配置时系统自动进行的认证内容,但是这是个字典的内容,我们为什么能直接点出它的内容呢,我们发现在这个类中有个__getter__方法

技术分享图片

在这个方法中,我们发现他通过val = self.defaults[attr]直接取值了,那么上面还有一个val = self.user_settings[attr],当它取值出错时才会去默认的defaults中取值

那么这个user_settings是什么呢

技术分享图片

可以看到它就是我们在settings中配置的REST_FRAMEWORK,所以当我们要做全局认证时只要在settings中配置REST_FRAMEWORK并且里面的键叫做DEFAULT_AUTHENTICATION_CLASSES即可

权限组件

局部视图权限

技术分享图片
from rest_framework.permissions import BasePermission
class SVIPPermission(BasePermission):
    message="SVIP才能访问!"
    def has_permission(self, request, view):
        if request.user.user_type==3:
            return True
        return False
技术分享图片

权限组件的原理与上面的认证组件类似,这里就不再赘述,可以参考源码

可以看到也是先定义一个权限类,类中必须有has_permission方法,这个方法返回Ture或者False

from app01.service.permissions import *

class BookViewSet(generics.ListCreateAPIView):
    permission_classes = [SVIPPermission,]
    queryset = Book.objects.all()
    serializer_class = BookSerializers

在视图类中同样是加一个参数permission_classes

全局视图权限

在settings中配置

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",]
}

rest-framework框架的基本组件

标签:message   过滤   iss   linked   重新定义   exp   http   name   inview   

原文地址:https://www.cnblogs.com/QQ279366/p/8782499.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!