yaya

今天也要加油鸭!!!

源码分析

认证组件

先看看源码查看源码趴👇

self是Request产生的对象,并不是APView产生的对象

证明以下就是列表中放的一个个对象

注意:response一定要到settings中注册"rest_framework"

-认证组件
-写一个认证类,继承BaseAuthentication
class MyAuth(BaseAuthentication):
def authenticate(self,request):
#写一些认证的逻辑
# print('我是认证类中的方法,只要配置了,一定会走我')
token=request.GET.get('token')
token_obj=models.Token.objects.filter(token=token).first()
if token_obj:
#有值表示登录了
#token_obj.user 当前登录的user对象
return token_obj.user,token_obj
else:
#没有值,表示没有登录,抛异常
raise AuthenticationFailed('您没有登录')

登录接口token的使用

md5算法目前还没有被破解,加^ -等加密方式都是破解不了的

-登录接口编写:
-models.py
from django.db import models

# Create your models here.


class User(models.Model):
    name=models.CharField(max_length=32)
    pwd=models.CharField(max_length=64)
    user_type=models.IntegerField(choices=((1,"超级管理员"),(2,"普通管理员"),(3,"2b用户")),default=3)
#跟User表做一对一关联
#token本来也可以写在User表中的,但是存在一张表中比较好
class Token(models.Model):
    #一对一的关系也是foreignkey只是加多了一个kwargs['unique']=True
    user=models.OneToOneField(to='User')
    token = models.CharField(max_length=64)

-views.py
 
post请求也是发送数据的,因为考虑到密文问题,也就是安全问题不采用get,然后post还有新增的请求功能
class Login(APIView): def post(self,request): response={'code':100,'msg':'登录成功'} name=request.data.get('name') pwd=request.data.get('pwd') try: #get 有且只有一条才不报错,其他都抛异常 user=models.User.objects.filter(name=name,pwd=pwd).get() #登录成功,需要去token表中存数据 #生成一个唯一的id token=uuid.uuid4() #有则更新,没有则创建 models.Token.objects.update_or_create(user=user,defaults={'token':token}) response['token']=token except ObjectDoesNotExist as e: response['code']=101 response['msg']='用户名或密码错误' except Exception as e: response['code'] = 102 # response['msg'] = '未知错误' response['msg'] = str(e) return Response(response)

-urls.py
url(r'^login/', views.Login.as_view()),

   🎡🎆🎡🐷👧😜😝以下是了解

一 什么是uuid

uuid是128位的全局唯一标识符(univeral unique identifier),通常用32位的一个字符串的形式来表现。有时也称guid(global unique identifier),C#语言中使用。python中自带了uuid模块来进行uuid的生成和管理工作

  • UUID —— Universally Unique IDentifier  Python中称为 UUID
  • GUID —— Globally Unique IDentifier     C#中称为 GUID

它是通过MAC地址、 时间戳、 命名空间、 随机数、 伪随机数来保证生成ID的唯一性,,有着固定的大小( 128 bit位 ),通常由 32 字节的字符串(十六进制)表示

它的唯一性和一致性特点,使得可以无需注册过程就能够产生一个新的UUID;UUID可以被用作多种用途, 既可以用来短时间内标记一个对象,也可以可靠的辨别网络中的持久性对象


二 uuid有什么用

很多应用场景需要一个id,但是又不要求这个id 有具体的意义,仅仅用来标识一个对象。常见的用处有数据库表的id字段;用户session的key值;前端的各种UI库,因为它们通常需要动态创建各种UI元素,这些元素需要唯一的id, 这时候就需要使用UUID了。例如:一个网站在存储视频、图片等格式的文件时,这些文件的命名方式就可以采用 UUID生成的随机标识符,避免重名的出现


三 Python中uuid模块

UUID主要有五个算法,也就是五种方法来实现

python的uuid模块提供的UUID类和函数uuid1(),uuid3(),uuid4(),uuid5() 来生成1, 3, 4, 5各个版本的UUID ( 需要注意的是:python中没有uuid2()这个函数)。

  • uuid.uuid1(node clock_seq)
复制代码
#   基于时间戳
#   使用主机ID, 序列号, 和当前时间来生成UUID, 可保证全球范围的唯一性.
#   但由于使用该方法生成的UUID中包含有主机的网络地址, 因此可能危及隐私.
#   该函数有两个参数, 如果 node 参数未指定, 系统将会自动调用 getnode() 函数来获取主机的硬件(mac)地址.
#   如果 clock_seq  参数未指定系统会使用一个随机产生的14位序列号来代替.
import uuid
print(uuid.uuid1())
复制代码
  • uuid.uuid3(namespace, name)
#   通过计算名字和命名空间的MD5散列值得到,保证了同一命名空间中不同名字的唯一性,
#   和不同命名空间的唯一性,***但同一命名空间的同一名字生成相同的uuid****。
print(uuid.uuid3(uuid.NAMESPACE_URL,'python'))
print(uuid.uuid3(uuid.NAMESPACE_URL,'python'))
  • uuid.uuid4() : 基于随机数
#   通过随机数来生成UUID. 使用的是伪随机数有一定的重复概率.
print(uuid.uuid4())
  • uuid.uuid5(namespace, name)
#   通过计算命名空间和名字的SHA-1散列值来生成UUID, 算法与 uuid.uuid3() 相同
print(uuid.uuid5(uuid.NAMESPACE_URL,'python'))

四 总结

1 Python中没有基于 DCE 的,所以uuid2可以忽略
2 uuid4存在概率性重复,由无映射性
3 若在Global的分布式计算环境下,最好用uuid1
4 若有名字的唯一性要求,最好用uuid3或uuid5

☺❣💥❗🐷😱👌😊以上是了解

认证的局部使用,全局使用,局部禁用

Myauth.py 自创的认证类中的文件

from rest_framework.authentication import BaseAuthentication
from app01 import models
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import BasePermission
class MyAuth(BaseAuthentication):
    #authenticate重写了BaseAuthentication的方法
    def authenticate(self,request):
        #写一些认证的逻辑
        # print('我是认证类中的方法,只要配置了,一定会走我')
        token=request.GET.get('token')
        token_obj=models.Token.objects.filter(token=token).first()
        if token_obj:
            #有值表示登录了
            #token_obj.user 当前登录的user对象
            return token_obj.user,token_obj
        else:
            #没有值,表示没有登录,抛异常
            raise AuthenticationFailed('您没有登录')
from django.shortcuts import render,HttpResponse

# Create your views here.

from rest_framework.views import  APIView
from rest_framework.response import Response
from rest_framework.request import Request
from django.core.exceptions import ObjectDoesNotExist
from app01 import  models
from rest_framework.exceptions import AuthenticationFailed
import uuid
from rest_framework.authentication import BaseAuthentication

from app01.MyAuths import MyAuth,MyPermision

#用户必须登录之后才能访问获取所有图书接口
class Books(APIView):
    #可以写多个认证类
    # authentication_classes=[MyAuth,]
    #只有超级用户才能访问该接口
    permission_classes=[MyPermision,]
    def get(self,request):
        #request.user 就是当前登录用户
        print(request.user.name)
        return Response('返回了所有图书')

class Publish(APIView):
    # authentication_classes = [MyAuth, ]
    permission_classes=[]
    def get(self,request):
        print(request.user.name)
        return Response('返回了所有出版社信息')
class Login(APIView):
    authentication_classes = []
    def post(self,request):
        response={'code':100,'msg':'登录成功'}
        name=request.data.get('name')
        pwd=request.data.get('pwd')
        try:
            #get 有且只有一条才不报错,其他都抛异常
            user=models.User.objects.filter(name=name,pwd=pwd).get()
            #登录成功,需要去token表中存数据
            #生成一个唯一的idhg
            token=uuid.uuid4()  #可以被破解,但是破解的几率比你中彩票的几率还低
            models.Token.objects.update_or_create(user=user,defaults={'token':token})
            response['token']=token  
        except ObjectDoesNotExist as e:
            response['code']=101
            response['msg']='用户名或密码错误'
        except Exception as e:
            response['code'] = 102
            # response['msg'] = '未知错误'
            response['msg'] = str(e)
        return Response(response)
Views.py

 

-局部使用
-在视图类中写
authentication_classes=[MyAuth,]
-全局使用
在settings.py中配置
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.MyAuths.MyAuth",]
}
-局部禁用:
authentication_classes = []

-读源码看到的东西:
-如果在项目的setting.py中配置了REST_FRAMEWORK,默认先从项目的setting中取
-如果取不到,才去默认的drf配置文件中取
-如果用户在视图类中配置了某个,先去用户配置的取

总结:
先取视图类中配置的----》项目setting中取----》默认配置


以下是源码部分👇💥

以下是自定义校验类中函数的返回值也就是元类中user_auth_tuple

request是元类中Request产生的新对象,对应以上,所以request可以点user获取到用户,而且是当前通过校验的用户

 

展示一下get请求方法

权限类的使用

源码👇👇

Myauth.py 

#自定义的检验类
#重写的has_permission方法,根据源码传入需要传入的参数
class MyPermision(BasePermission):
    message = '不是超级用户,查看不了'
    def has_permission(self,request,view):
        if request.user.user_type==1:
            return True
        else:
            return False

view.py

from django.shortcuts import render,HttpResponse

# Create your views here.

from rest_framework.views import  APIView
from rest_framework.response import Response
from rest_framework.request import Request
from django.core.exceptions import ObjectDoesNotExist
from app01 import  models
from rest_framework.exceptions import AuthenticationFailed
import uuid
from rest_framework.authentication import BaseAuthentication

from app01.MyAuths import MyAuth,MyPermision

#用户必须登录之后才能访问获取所有图书接口
class Books(APIView):
    #可以写多个认证类
    # authentication_classes=[MyAuth,]
    #只有超级用户才能访问该接口
    permission_classes=[MyPermision,]  局部使用
    def get(self,request):
        #request.user 就是当前登录用户
        print(request.user.name)
        return Response('返回了所有图书')

class Publish(APIView):
    # authentication_classes = [MyAuth, ]
    permission_classes=[]  局部禁用
    def get(self,request):
        print(request.user.name)
        return Response('返回了所有出版社信息')

#表示注释掉的

想把错误信息改成中文的

分析源码👇

myauth.py

#自定义的检验类
#重写的has_permission方法,根据源码传入需要传入的参数
class MyPermision(BasePermission):
    message = '不是超级用户,查看不了'
    def has_permission(self,request,view):
        if request.user.user_type==1:
            return True
        else:
            return False

views.py

from django.shortcuts import render,HttpResponse

# Create your views here.

from rest_framework.views import  APIView
from rest_framework.response import Response
from rest_framework.request import Request
from django.core.exceptions import ObjectDoesNotExist
from app01 import  models
from rest_framework.exceptions import AuthenticationFailed
import uuid
from rest_framework.authentication import BaseAuthentication

from app01.MyAuths import MyAuth,MyPermision

#用户必须登录之后才能访问获取所有图书接口
class Books(APIView):
    #可以写多个认证类
    # authentication_classes=[MyAuth,]
    #只有超级用户才能访问该接口
    permission_classes=[MyPermision,]  局部使用
    def get(self,request):
        #request.user 就是当前登录用户
        print(request.user.name)

访问图书,需要通过权限认证的,没有权限就抛出异常

小总结:

-权限
-写一个权限类,继承BasePermission
class MyPermision(BasePermission):
message = '不是超级用户,查看不了'
def has_permission(self,request,view):
#因为权限在认证之后执行的,所有能取到reuqest.user
if request.user.user_type==1:
return True
else:
return False
-局部使用
-在视图类中写
permission_classes=[MyPermision,]
-全局使用
在settings.py中配置
REST_FRAMEWORK={
"DEFAULT_PERMISSION_CLASSES":["app01.MyAuths.MyPermision",]
}
-局部禁用:
permission_classes = []
-读源码看到的东西:
-返回值显示中文,写个message=“中文”

视图

第一种方式✨

from django.db import models

# Create your models here.
class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish_date = models.DateField(null=True)
    xx=models.IntegerField(choices=((0,'文学类'),(1,'情感类')),default=1,null=True)
    publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE,null=True)
    authors=models.ManyToManyField(to='Author')
    def __str__(self):
        return self.name
    def test(self):
        return 'xxx'


class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()



class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.name
models.py
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework import serializers
from app01 import models
from rest_framework.views import APIView
# Create your views here.


class PublishSerializers(serializers.ModelSerializer):
    class Meta:
        model=models.Publish
        fields='__all__'
#基本视图
class PublishView(APIView):

    def get(self, request):
        publish_list = models.Publish.objects.all()
        bs = PublishSerializers(publish_list, many=True)
        # 序列化数据

        return Response(bs.data)

    def post(self, request):
        # 添加一条数据
        print(request.data)

        bs=PublishSerializers(data=request.data)
        if bs.is_valid():
            bs.save()  # 生成记录
            return Response(bs.data)
        else:

            return Response(bs.errors)

class PublishDetailView(APIView):
    def get(self,request,pk):
        publish_obj=models.Publish.objects.filter(pk=pk).first()
        bs=PublishSerializers(publish_obj,many=False)
        return Response(bs.data)
    def put(self,request,pk):
        publish_obj = models.Publish.objects.filter(pk=pk).first()

        bs=PublishSerializers(data=request.data,instance=publish_obj)
        if bs.is_valid():
            bs.save() # update
            return Response(bs.data)
        else:
            return Response(bs.errors)
    def delete(self,request,pk):
        models.Publish.objects.filter(pk=pk).delete()

        return Response("")
views.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^publish/$', views.PublishView.as_view()),
    url(r'^publish/(?P<pk>\d+)/$', views.PublishDetailView.as_view()),
]
url.py

第二种方式💥

views.py 基于mixin来封装的视图

from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin,ListModelMixin,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin
#继承了CreateModelMixin就不需要写保存的逻辑了,直接调用内部封装好的save()方法
class PulishView(CreateModelMixin,ListModelMixin,GenericAPIView):
  #对应createmodelsmixin的新增方法
def post(self,reqeust,*args,**kwargs): return self.create(reqeust, *args,**kwargs)
  #对应的是listmodelmixin的获取到列表存放多条数据
def get(self, request,*args, **kwargs): return self.list(request, *args, **kwargs) class PulishDetailView(RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin,GenericAPIView):
  对应的是RetrieveModelMixin,获取到单条数据
def get(self,request,*args,**kwargs): return self.retrieve(request, *args,**kwargs)
  对应的是UpdateModelMixin,更新修改单条数据
def put(self, request, *args,**kwargs): return self.update(request,*args,**kwargs)
  对应的是DestroyModelMixin,删除单条数据
def delete(self, request,*args, **kwargs): return self.delete(request,*args, **kwargs)

源码

CreateModelMixin

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    #self.get_serializer(data=request.data)序列化的类, serializer序列化的对象
    def create(self, request, *args, **kwargs):
     🍓#缺少get_serializer方法🍓 serializer
= self.get_serializer(data=request.data) #进行校验 serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) #201表示请求成功,headers=headers在响应头放入的东西 return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer): serializer.save()
listmodelmixin

class ListModelMixin(object):
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        #通过过滤之后将queryset取出 🍓这里缺少get_queryset()方法🍓
        queryset = self.filter_queryset(self.get_queryset())
        #到配置里通过分页等到了需要序列化的数据,假如数据很大,
        # 就进行分页,一页十条,也就是十条queryset
        page = self.paginate_queryset(queryset)
        if page is not None:
            #page是queryset对象,需要序列化的对象
            serializer = self.get_serializer(page, many=True)
            #返回serializer.data
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
RetrieveModelMixin
class RetrieveModelMixin(object):
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        🍓#获取单挑条,然后点击进去不能点击进去,说明缺少这个方法,必须要自己自定义补上🍓
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

🍓源码中三个缺少的东西🍓

所以说还得继承一个类GenericAPIView(在源码中找到缺失的方法)

在CreateModelMixin中缺少get_serializer,还缺少queryset以及serializer_classes
所以还要继承GenericAPIView
class GenericAPIView(views.APIView):
    """
    Base class for all other generic views.
    """
    # You'll need to either set these attributes,
    # or override `get_queryset()`/`get_serializer_class()`.
    # If you are overriding a view method, it is important that you call
    # `get_queryset()` instead of accessing the `queryset` property directly,
    # as `queryset` will get evaluated only once, and those results are cached
    # for all subsequent requests.
    🌴queryset = None🌴   #以下两个函数的querset,serializer的返回值就是等于None,
所以需要我们在自定义类中的变量进行赋值操作再传入 🌴💥serializer_class
= None🌴💥 # If you want to use object lookups other than pk, set 'lookup_field'. # For more complex lookup requirements override `get_object()`. lookup_field = 'pk' lookup_url_kwarg = None # The filter backend classes to use for queryset filtering filter_backends = api_settings.DEFAULT_FILTER_BACKENDS # The style to use for queryset pagination. pagination_class = api_settings.DEFAULT_PAGINATION_CLASS 🍓def get_queryset(self):🍓 """ Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using `self.queryset`. This method should always be used rather than accessing `self.queryset` directly, as `self.queryset` gets evaluated only once, and those results are cached for all subsequent requests. You may want to override this if you need to provide different querysets depending on the incoming request. (Eg. return a list of items that is specific to the user) """ assert self.queryset is not None, ( "'%s' should either include a `queryset` attribute, " "or override the `get_queryset()` method." % self.__class__.__name__ ) queryset = self.queryset if isinstance(queryset, QuerySet): # Ensure queryset is re-evaluated on each request. queryset = queryset.all() 🌴return queryset🌴 🍓def get_object(self):🍓 """ Returns the object the view is displaying. You may want to override this if you need to provide non-standard queryset lookups. Eg if objects are referenced using multiple keyword arguments in the url conf. """ queryset = self.filter_queryset(self.get_queryset()) # Perform the lookup filtering. lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field assert lookup_url_kwarg in self.kwargs, ( 'Expected view %s to be called with a URL keyword argument ' 'named "%s". Fix your URL conf, or set the `.lookup_field` ' 'attribute on the view correctly.' % (self.__class__.__name__, lookup_url_kwarg) ) filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]} obj = get_object_or_404(queryset, **filter_kwargs) # May raise a permission denied self.check_object_permissions(self.request, obj) return obj 🍓def get_serializer(self, *args, **kwargs):🍓 """ Return the serializer instance that should be used for validating and deserializing input, and for serializing output. """ serializer_class = self.get_serializer_class() kwargs['context'] = self.get_serializer_context() 🌴💥return serializer_class(*args, **kwargs)🌴💥 a 🌴💥def get_serializer_class(self):🌴💥 """ Return the class to use for the serializer. Defaults to using `self.serializer_class`. You may want to override this if you need to provide different serializations depending on the incoming request. (Eg. admins get full serialization, others get basic serialization) """ assert self.serializer_class is not None, ( "'%s' should either include a `serializer_class` attribute, " "or override the `get_serializer_class()` method." % self.__class__.__name__ ) 🌴💥return self.serializer_class🌴💥

配好参数的视图

views.py

from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin,ListModelMixin,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin
#继承了CreateModelMixin就不需要写保存的逻辑了,直接调用内部封装好的save()方法
class PulishView(CreateModelMixin,ListModelMixin,GenericAPIView):
    queryset = models.Publish.objects.all()
    serializers_class=PublishSerializers
    def post(self,reqeust,*args,**kwargs):
        return self.create(reqeust, *args,**kwargs)
    def get(self, request,*args, **kwargs):
        return self.list(request, *args, **kwargs) #点击list查看获取所有的时候get_queryset()
class PulishDetailView(RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin,GenericAPIView):
    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.delete(request,*args, **kwargs)

配好之后再看源码

 def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        #调用了自身的get_serializer_class()
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.

        You may want to override this if you need to provide different
        serializations depending on the incoming request.

        (Eg. admins get full serialization, others get basic serialization)
        """
        #断言必然不能为空
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )
get_serializer
class GenericAPIView(views.APIView):
    """
    Base class for all other generic views.
    """
    # You'll need to either set these attributes,
    # or override `get_queryset()`/`get_serializer_class()`.
    # If you are overriding a view method, it is important that you call
    # `get_queryset()` instead of accessing the `queryset` property directly,
    # as `queryset` will get evaluated only once, and those results are cached
    # for all subsequent requests.
    queryset = None
    serializer_class = None  #此时获取到PublishSerializers

    # If you want to use object lookups other than pk, set 'lookup_field'.
    # For more complex lookup requirements override `get_object()`.
    lookup_field = 'pk'
    lookup_url_kwarg = None

    # The filter backend classes to use for queryset filtering
    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS

    # The style to use for queryset pagination.
    pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

    def get_queryset(self):
        """
        Get the list of items for this view.
        This must be an iterable, and may be a queryset.
        Defaults to using `self.queryset`.

        This method should always be used rather than accessing `self.queryset`
        directly, as `self.queryset` gets evaluated only once, and those results
        are cached for all subsequent requests.

        You may want to override this if you need to provide different
        querysets depending on the incoming request.

        (Eg. return a list of items that is specific to the user)
        """
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )

        queryset = self.queryset
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()
        return queryset

    def get_object(self):
        """
        Returns the object the view is displaying.

        You may want to override this if you need to provide non-standard
        queryset lookups.  Eg if objects are referenced using multiple
        keyword arguments in the url conf.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        #调用了自身的get_serializer_class() ,此时的serializer_class为PublishSerializers
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        #PublishSerializers(*args, **kwargs)返回去
        return serializer_class(*args, **kwargs)

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.

        You may want to override this if you need to provide different
        serializations depending on the incoming request.

        (Eg. admins get full serialization, others get basic serialization)
        """
        #断言必然不能为空
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )

        return self.serializer_class
源码,注释
queset源码分析

第三种方式🍖

from rest_framework.generics import CreateAPIView,ListCreateAPIView,DestroyAPIView,RetrieveUpdateDestroyAPIView
class PublishView(ListCreateAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = PublishSerializers

class PublishDetailView(RetrieveUpdateDestroyAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = PublishSerializers

源码

CreateAPIView

继承了两个类,如果只是写post方法的话就继承这个CreateAPIView就好了
class
CreateAPIView(mixins.CreateModelMixin, GenericAPIView): """ Concrete view for creating a model instance. """ def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)
ListCreateAPIView

继承了三个类,即可以同时使用get和post两种方法都给封装好了,重写了get和post
class
ListCreateAPIView(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView): """ Concrete view for listing a queryset or creating a model instance. """ def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)
DestroyAPIView
只写了delete方法
class
DestroyAPIView(mixins.DestroyModelMixin, GenericAPIView): """ Concrete view for deleting a model instance. """ def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)
RetrieveUpdateDestroyAPIView
三种方法都重写了
class
RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, GenericAPIView): """ Concrete view for retrieving, updating or deleting a model instance. """ 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 patch(self, request, *args, **kwargs): return self.partial_update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)

第四种写法💪

五个接口写在一个类中

url.py

urlpatterns = [
    url(r'^admin/', admin.site.urls),
  
    url(r'^publish/$', views.PublishView.as_view({'get': 'list', 'post': 'create'})),
    url(r'^publish/(?P<pk>\d+)/$',views.PublishView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]

views.py

from django.views import View
from rest_framework.viewsets import ModelViewSet
class PublishView(ModelViewSet):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers

查看ViewSetMixin

之前继承view中的as_view方法只是传入一个参数

源码分析,注意看注释(重点看view方法)

class ViewSetMixin(object):
    """
    This is the magic.

    Overrides `.as_view()` so that it takes an `actions` keyword that performs
    the binding of HTTP methods to actions on the Resource.

    For example, to create a concrete view binding the 'GET' and 'POST' methods
    to the 'list' and 'create' actions...
    重写了as_view的方法✴👱‍♀️
    view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
    """

    @classonlymethod✈😭
    #相比之前的参数多了一个action=None的参数 ,actions就是路由中配置的{'get':'list','post':'create'}
    def as_view(cls, actions=None, **initkwargs):
        """
        Because of the way class based views create a closure around the
        instantiated view, we need to totally reimplement `.as_view`,
        and slightly modify the view function that is created and returned.
        """
        # The name and description initkwargs may be explicitly overridden for
        # certain route confiugurations. eg, names of extra actions.
        cls.name = None
        cls.description = None

        # The suffix initkwarg is reserved for displaying the viewset type.
        # This initkwarg should have no effect if the name is provided.
        # eg. 'List' or 'Instance'.
        cls.suffix = None

        # The detail initkwarg is reserved for introspecting the viewset type.
        cls.detail = None

        # Setting a basename allows a view to reverse its action urls. This
        # value is provided by the router through the initkwargs.
        cls.basename = None

        # actions must not be empty 没有字典就会抛异常
        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")

        # sanitize keyword arguments
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r" % (
                    cls.__name__, key))

        # name and suffix are mutually exclusive
        if 'name' in initkwargs and 'suffix' in initkwargs:
            raise TypeError("%s() received both `name` and `suffix`, which are "
                            "mutually exclusive arguments." % (cls.__name__))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)🎭🥨
            # We also store the mapping of request methods to actions,
            # so that we can later set the action attribute.
            # eg. `self.action = 'list'` on an incoming GET request.
            self.action_map = actions

            # Bind methods to actions
            # This is the bit that's different to a standard view
            # {'get': 'list', 'post': 'create'}  循环取出字典的key,vaule值
            for method, action in actions.items():
                #通过反射self中的action,也就是先找'list'
                # 获取到list的方法,(self是PublishView的对象)
                #而PublishView类里面是有list的,handler就是list的内存地址
                handler = getattr(self, action)
                # 把list的内存地址,也就是handler给了method,也就是get
                #路由变成了发送get请求,其实执行的是list的方法
                #这样就实现了一个视图类对应多个接口
                setattr(self, method, handler)

            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get

            self.request = request
            self.args = args
            self.kwargs = kwargs

            # And continue as usual
            return self.dispatch(request, *args, **kwargs)

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())

        # We need to set these on the view function, so that breadcrumb
        # generation can pick out these bits of information from a
        # resolved URL.
        view.cls = cls
        view.initkwargs = initkwargs
        view.actions = actions
        return csrf_exempt(view)

如果觉得list不适合我的保存数据的规则(它保存到一个表,而我需要保存到多个表),要重写保存的方式

第一种就是重写create方法

第二种就是继承ModelViewSet,然后修改其中的ViewSetMixin中的as_view重写的方法

改变我路由的方式

ViewSetMixin继承的类是object,并没有继承view或者APIView,

所以还是要继承APIView.

from rest_framework.viewsets import  ViewSetMixin
from rest_framework.views import  APIView
# ViewSetMixin 重写了as_view方法
#两个类中都有as_view,有先后顺序,把ViewSetMixin放在前面,才会先调用
class Test(ViewSetMixin,APIView):

    def aaa(self,request):
        return Response()

url.pyurlpatterns = [

    url(r'^admin/', admin.site.urls),
    # url(r'^publish/$', views.PublishView.as_view()),
    # url(r'^publish/(?P<pk>\d+)/$', views.PublishDetailView.as_view()),
  get请求来aa,就走对应的aaa
  get请求来bb,就走bb
url(r'^aa/$', views.PublishView.as_view({'get': 'aaa'})), url(r'^bb/$', views.PublishView.as_view({'get': 'bbb'})), url(r'^publish/$', views.PublishView.as_view({'get': 'list', 'post': 'create'})), url(r'^publish/(?P<pk>\d+)/$',views.PublishView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})), ]

 

posted @ 2019-07-04 06:42  Tiffany'.'  阅读(821)  评论(0编辑  收藏  举报