郑统京 发表于 2022-12-25 20:59:26

Django中drf的自动生成路由和认证源码刨析

## 1 路由
# 1 在urls.py中配置
    path('books4/', views.Book4View.as_view()),
    re_path('books4/(?P<pk>\d+)', views.Book4DetailView.as_view()),
# 2 一旦视图类,继承了ViewSetMixin,路由
    path('books5/', views.Book5View.as_view(actions={'get':'list','post':'create'})), #当路径匹配,又是get请求,会执行Book5View的list方法
    re_path('books5/(?P<pk>\d+)', views.Book5View.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})),

# 3 继承自视图类,ModelViewSet的路由写法(自动生成路由)
-urls.py
    # 第一步:导入routers模块
    from rest_framework import routers
    # 第二步:有两个类,实例化得到对象
    # routers.DefaultRouter 生成的路由更多
    # routers.SimpleRouter
    router=routers.DefaultRouter()
    SimpleRouter会生成两条路由
    <URLPattern '^books/$' >,
    <URLPattern '^books/(?P<pk>[^/.]+)/$'>
    DefaultRouter生成6条路由
    ^books/$
    ^books/(?P<pk>[^/.]+)/$ 这两条跟simple一样
    ^$ 根,根路径会显示出所有可以访问的地址
    ^\.(?P<format>+)/?$
    ^books\.(?P<format>+)/?$ http://127.0.0.1:8000/books.json
    ^books/(?P<pk>[^/.]+)\.(?P<format>+)/?$ http://127.0.0.1:8000/books/1.json
-
    # 第三步:注册
    # router.register('前缀','继承自ModelViewSet视图类','别名')
    router.register('books',views.BookViewSet) # 不要加斜杠了
    # 第四步
    # router.urls # 自动生成的路由,加入到原路由中
    # print(router.urls)
    # urlpatterns+=router.urls[列表相加,也可以用for循环就是比较麻烦,要for循环两次],[字典相加用update方法]
    '''
-views.py
    from rest_framework.viewsets import ModelViewSet
    from app01.models import Book
    from app01.ser import BookSerializer
    class BookViewSet(ModelViewSet):
      queryset =Book.objects
      serializer_class = BookSerializer

### 1.1 action的使用

# action干什么用?为了给继承自ModelViewSet的视图类中定义的函数也添加路由
# 使用
class BookViewSet(ModelViewSet):
    queryset =Book.objects.all()
    serializer_class = BookSerializer
    # methods第一个参数,传一个列表,列表中放请求方式,
    # ^books/get_1/$ 当向这个地址发送get请求,会执行下面的函数
    # detail:布尔类型 如果是True
    #^books/(?P<pk>[^/.]+)/get_1/$
    @action(methods=['GET','POST'],detail=True)
    def get_1(self,request,pk):
      print(pk)
      book=self.get_queryset()[:2]# 从0开始截取一条
      ser=self.get_serializer(book,many=True)
      return Response(ser.data)
   
# 装饰器,放在被装饰的函数上方,method:请求方式,detail:是否带pk


### 2.2认证的源码分析
#1 APIVIew----》dispatch方法---》self.initial(request, *args, **kwargs)包装成新的reqeust---->有认证,权限,频率
#2 只读认证源码: self.perform_authentication(request) 这个目前还在APIview类中,找还是先从自己的视图类中找
#3 self.perform_authentication(request)就一句话:request.user,需要去drf的Request对象中找user属性(方法)
#4 Request类中的user方法,刚开始来,没有_user,走 self._authenticate(),通过drf的Request中发现.user有@name.setattr这个是放值,我们要用取值的方法
def user(self):
    if no hasattr(self,'_user'):如果request没有_user走下面判断
      with wrap_attributeerrors():
            self._authenticate() 走Request的这个方法
    return self._user
#5 核心,就是Request类的 _authenticate(self):
    def _authenticate(self):
      # self.authenticators配置的一堆认证类产生的认证类对象组成的 list
      # self.authenticators 你在视图类中配置的一个个的认证类:authentication_classes=[认证类1,认证类,对象的列表
      for authenticator in self.authenticators:
            [# self.authenticators---》先去Request中找,发现在__init__中,当作参数传进来的,说明在初始化的时候被传进来的--》上一层dispatch中,发现在APIview的里面中有这个方法get_authenticators方法,是一个列表生成式[对象1,对象]
            try:
                # 认证器(对象)调用认证方法authenticate(认证类对象self, request请求对象)
                # 返回值:登陆的用户与认证的信息组成的 tuple
                # 该方法被try包裹,代表该方法会抛异常,抛异常就代表认证失败
                user_auth_tuple = authenticator.authenticate(self) # [注意这self是Request对象所以写authenticate时会把request也会闯过了,认证对象调用自己的authenticate方法也会把认证类自动传进来]
            except exceptions.APIException:
                self._not_authenticated()
                raise
            # 返回值的处理
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                # 如何有返回值,就将 登陆用户 与 登陆认证 分别保存到 request.user、request.auth
                self.user, self.auth = user_auth_tuple
                return
      # 如果返回值user_auth_tuple为空,代表认证通过,但是没有 登陆用户 与 登陆认证信息,代表游客
      self._not_authenticated()

### 2.3 认证组件的使用

# 写一个认证类 app_auth.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from app01.models import UserToken
class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
      # 认证逻辑,如果认证通过,返回两个值
      #如果认证失败,抛出AuthenticationFailed异常
      token=request.GET.get('token')
      iftoken:
            user_token=UserToken.objects.filter(token=token).first()
            # 认证通过
            if user_token:
                return user_token.user,token这个就是为什么request.user可以拿到当前用户的对象,.user跨表到User用户对象里面去了]
            else:
                raise AuthenticationFailed('认证失败')
      else:
            raise AuthenticationFailed('请求地址中需要携带token')

# 可以有多个认证,从左到右依次执行
# 全局使用,在setting.py中配置
REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",]
}
# 局部使用,在视图类上写
authentication_classes=
# 局部禁用
authentication_classes=[]
页: [1]
查看完整版本: Django中drf的自动生成路由和认证源码刨析