3 上节总结、视图三部曲、认证组件

https://www.cnblogs.com/yuanchenqi/articles/8719520.html

https://www.cnblogs.com/alice-bj/p/9252207.html

1、上节总结

1.上节笔记

1.Django的原生request:

request.body
request.POST

浏览器   -------------  服务器

    "GET url?a=1&b=2 http/1.1\r\user_agent:Google\r\ncontentType:urlencoded\r\n\r\n"
    "POST url http/1.1\r\user_agent:Google\r\ncontentType:urlencoded\r\n\r\na=1&b=2"
    
    requst.body:b"a=1&b=2"
    
    if POST请求:
        if contentType == "urlencoded":
            request.POST = {request.body} = {"a":1,"b":2}
    if GET请求:
        request = {}  # get没有请求体
        request.GEt = {url?a=1&b=2}
        
2.APIView

    re_path(r'books/(\d+)/$', views.BookDetailView.as_view()),
    re_path(r'books/(\d+)/$', View类下的view函数),
    
    if 用户访问books/3/:
        view函数(request) == APIView类下的dispatch(request)
        
3.构建的新的dispatch

    def dispatch(request):
        # 构建一个新的request
        request = self.initialize_request(request, *args, **kwargs)
        # request.data  # POST PUT
        # request.GET   # GET
        
        # 分发,  handler就是get post的执行
        # 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)
                              
            response = handler(request, *args, **kwargs)
            return self.response
            
            
            def get(self,request,id):
                # 获取某本书的信息
                book = Book.objects.filter(pk=id).first()  # 过滤单挑data
                bs = BookModelSerializers(book,context={'request':request})
                return Response(bs.data)
            
4.序列化组件
    
    class BookModelSerializers(serializers.ModelSerializer):  # ModelSerializer
        class Meta:
            model = Book
            fields = '__all__'
    
    
    # 将 querset/model_obj ====> 序列化数据
    bs = BookModelSerializers(book_list,many=True)
    bs = BookModelSerializers(model_obj,many=False)

    
    # 将序列化数据===>queryset====>数据记录
    bs = BookModelSerializers(data=request.data)
    if bs.is_valid():
        print(bs.validated_data)
        bs.save()  # create方法
        return Response(bs.data)
    
    
    book = Book.objects.filter(pk=id).first()
    bs = BookModelSerializers(book,data=request.data)
    if bs.is_valid():
        bs.save()  # update方法
        return Response(bs.data)
    
    
5.对某个model表进行数据处理(增删改查)

    class BookView(APIView):
        def get(self,request):
            book_list = Book.objects.all()
            bs = BookModelSerializers(book_list,many=True,context={'request':request})
            return Response(bs.data)  # Response继承HttpResponse

2.遗留问题

存在太多的代码重复使用,如何进行代码复用?

视图三部曲

2.视图三部曲

2.1 视图1:混合(mixins)

上节的视图内容

model

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",on_delete=models.CASCADE)
    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
        return self.email

class Author(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField()
    def __str__(self):
        return self.name
View Code

 url

from django.contrib import admin
from django.urls import path
from django.urls import re_path  # 正则表达式的

from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),

    path('publishes/', views.PublishView.as_view()), # view(request)====> APIView:dispatch()
    re_path(r'publishes/(?P<pk>\d+)/$', views.PublishDetailView.as_view(),name="detailPublish"),

    path('books/', views.BookView.as_view()),
    re_path(r'books/(\d+)/$', views.BookDetailView.as_view()),
]
View Code

 serializer

from rest_framework import serializers  # rest_framework的序列化组件

from .models import Book,Publish

class PublishSerializers(serializers.Serializer):
    name = serializers.CharField()
    email = serializers.EmailField()


class BookModelSerializers(serializers.ModelSerializer):  # ModelSerializer
    class Meta:
        model = Book
        fields = '__all__'

    # 显示超链接,在Book下的publish
    publish = serializers.HyperlinkedIdentityField(
        view_name="detailPublish", # 别名 含正则表达式
        lookup_field="publish_id",   # publish_id替换pk
        lookup_url_kwarg="pk",    # url中的pk
    )

class PublishModelSerializers(serializers.ModelSerializer):  # ModelSerializer
    class Meta:
        model = Publish
        fields = '__all__'
View Code

 view

from django.shortcuts import render,HttpResponse

from rest_framework.views import APIView
from rest_framework.response import Response

from app01.serilizer import BookModelSerializers  # 从serilizer中导入
from app01.serilizer import PublishModelSerializers  #

from .models import Book,Publish


class PublishView(APIView):   # APIView
    def get(self,request):
        publish_list = Publish.objects.all()
        ps = PublishModelSerializers(publish_list,many=True)
        return Response(ps.data)

    def post(self,request):
        ps = PublishModelSerializers(data=request.data)
        if ps.is_valid():
            ps.save()
            return Response(ps.data)
        else:
            return Response(ps.errors)


class PublishDetailView(APIView):
    def get(self,request,pk):
        # 获取某publish的信息
        publish = Publish.objects.filter(pk=pk).first()
        ps = PublishModelSerializers(publish)
        return Response(ps.data)

    def put(self,request,pk):
        # 更新某pub的信息
        publish = Publish.objects.filter(pk=pk).first()
        ps = PublishModelSerializers(publish,data=request.data)
        if ps.is_valid():
            ps.save()
            return Response(ps.data)
        else:
            return Response(ps.errors)

    def delete(self,request,pk):
        # 删除某天publish
        Publish.objects.filter(pk=pk).delete()
        return Response("Delete 第%s个出版社"%(pk))


class BookView(APIView):
    def get(self,request):
        book_list = Book.objects.all()
        bs = BookModelSerializers(book_list,many=True,context={'request':request})
        return Response(bs.data)  # Response继承HttpResponse

    def post(self,request):
        # post请求的数据
        bs = BookModelSerializers(data=request.data,context={'request':request})
        if bs.is_valid():
            print(bs.validated_data)
            bs.save()  # create方法
            return Response(bs.data)
        else:
            return Response(bs.errors)


class BookDetailView(APIView):
    def get(self,request,id):
        # 获取某本书的信息
        book = Book.objects.filter(pk=id).first()  # 过滤单挑data
        bs = BookModelSerializers(book,context={'request':request})
        return Response(bs.data)

    def put(self,request,id):
        # 更新某本书的字段
        book = Book.objects.filter(pk=id).first()
        bs = BookModelSerializers(book,data=request.data,context={'request':request})
        if bs.is_valid():
            bs.save()  # 实质create方法
            return Response(bs.data)
        else:
            return Response(bs.errors)

    def delete(self,request,id):
        # 删除某条数据
        Book.objects.filter(pk=id).delete()
        return Response("Delete 第%s本书成功"%(id))
View Code

2.2 视图2:mixin类编写视图

添加Author

1.url

from django.contrib import admin
from django.urls import path
from django.urls import re_path  # 正则表达式的

from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
.....
path(
'authors/', views.AuthorView.as_view()), re_path(r'authors/(?P<pk>\d+)/$', views.AuthorDetailView.as_view()), ]

2.serilizer

from rest_framework import serializers  # rest_framework的序列化组件

from .models import Book,Publish,Author

class AuthorModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = '__all__'

3.view

from django.shortcuts import render,HttpResponse

from rest_framework.views import APIView
from rest_framework.response import Response



# Author
from .models import Author

from rest_framework import mixins
from rest_framework import generics

from app01.serilizer import AuthorModelSerializers

class AuthorView(mixins.ListModelMixin,mixins.CreateModelMixin,generics.GenericAPIView):
                       # list数据            # create数据              # 继承APIView
    queryset = Author.objects.all()  # queryset,serilizers 名称不能修改
    serializer_class = AuthorModelSerializers

    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 AuthorDetailView(mixins.RetrieveModelMixin,mixins.DestroyModelMixin,mixins.UpdateModelMixin,generics.GenericAPIView):
                                #恢复                   # delete                # update
    queryset = Author.objects.all()  # queryset,serilizers 名称不能修改
    serializer_class = AuthorModelSerializers

    # def get(self,request,id,*args,**kwargs):
    def get(self,request,*args,**kwargs): # 不写id,id怎么来的???
        return self.retrieve(request,*args,**kwargs)

    def delete(self,request,*args,**kwargs):
        return self.destroy(request,*args,**kwargs)

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

 

2.源码查看

 

 2. get请求

(1) queryset,serilizers 名称不能修改

queryset的源码,serializer_class源码

 (2)list,create

self.list

self.create

 

 

 3.put请求

is_vaild, save

 

 

 

2.3 视图3:通用的基于类的

views中authors重写

# Author
# 方法2
from .models import Author

from app01.serilizer import AuthorModelSerializers

from rest_framework import generics

class AuthorView(generics.ListCreateAPIView):
    # list数据            # create数据              # 继承APIView
    queryset = Author.objects.all()  # queryset,serilizers 名称不能修改
    serializer_class = AuthorModelSerializers

class AuthorDetailView(generics.RetrieveUpdateDestroyAPIView):
        # 恢复                   # delete                # update
        queryset = Author.objects.all()  # queryset,serilizers 名称不能修改
        serializer_class = AuthorModelSerializers

 

ListCreateAPIView继承了get post create

 

 

 

2.4 视图4:viewsets.ModelViewSet

1.代码

修改url 

from django.contrib import admin
from django.urls import path
from django.urls import re_path  # 正则表达式的

from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    ...

    path('authors/', views.AuthorView.as_view({"get":"list","post":"create"}),name="book_list"),
    re_path(r'authors/(?P<pk>\d+)/$', views.AuthorView.as_view({
            "get": "retrieve",
            "put": "update",
            "patch": "partial_update",
            "delete": "destroy"
            }),name="book_detail"),
]

 精简view

# Author
# 方法3:ModelViewSet
from .models import Author

from app01.serilizer import AuthorModelSerializers

from rest_framework import viewsets

class AuthorView(viewsets.ModelViewSet):
    # list数据            # create数据              # 继承APIView
    queryset = Author.objects.all()  # queryset,serilizers 名称不能修改
    serializer_class = AuthorModelSerializers

 

2. 源码剖析

(1)为什么要分开为2个url?

     因为,有2个get

 

(2)as_view不同了

基于类的视图:本质还是APIView下的as_view

ViewSetMixin类下,有自己的,as_view,有initkwargs,return 它的view

 

 

 

 (3)view函数源码,return  dispatch

  view反射到各自的  self.get,self.post,

hadler = self.list,self.create

setattr(self,"get",self.list )  赋值操作

return dispatch

 

 

 以后找get直接找self.list方法

 

 (4)dispatch怎么找

必须从最开始的类开始找,一步步找。

实质:其他父类都没有,dispatch是APIView,View下的dispatch

 

handler = list方法,return Response(list的内容)

 

 

2.4  三部曲总结

 小知识点

笔记

http://www.cnblogs.com/yuanchenqi/articles/8719520.html
视图三部曲
5中方法: 查(全部) 查(单条) 增 删 改
逻辑封装起来了

-----------------------------------
from rest_framework import viewsets

class AuthorModelView(viewsets.ModelViewSet):
    queryset = Author.objects.all()
    serializer_class = AuthorModelSerializers
    
url(r'^authors/$', views.AuthorModelView.as_view({"get":"list","post":"create"}), name='author'),

第一步::url(r'^authors/$', ViewSetMixin.as_view({"get":"list","post":"create"}), name='author'),
第二部:url(r'^authors/$', ViewsetMixin.View, name='author'),


一旦用户 get 方式 访问 authors:
ViewsetMixin.View():
    for method, action in actions.items(): # {"get":"list","post":"create"}
        handler = getattr(self, action)    # self.list  self.create
        setattr(self, method, handler)     # self.get = self.list  self.post = self.create
    
        # getattr(self,"get")  # self.list
        # getattr(self,"post") # self.create
    
    return self.dispatch()
    
APIView.dispatch():
    if request.method.lower() in self.http_method_names:
        handler = getattr(self,request.method.lower())
        response = handler(request,*args,**kwargs)  # self.list()
        
        return response

 执行效果

1.认证组件

三层csrftoken,是django自带的

工作中,用的最多的是自定制的token :就是一个字符串

1、登录与验证

1. models

添加user, token表

from django.db import models

class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)


class Token(models.Model):
    user = models.OneToOneField("user",on_delete=models.CASCADE)
    token = models.CharField(max_length=128)
    def __str__(self):
        return self.token

2.数据表迁移与生成

F:\PycharmProjects\restdemo>python manage.py makemigrations

F:\PycharmProjects\restdemo>python manage.py migrate

3. urls

    path('login/',views.LoginView.as_view())

4. views

from .models import User,Token
import json
class LoginView(APIView):
    def post(self,request):
        name = request.data.get('name')
        pwd = request.data.get('pwd')
        user = User.objects.filter(name=name,pwd=pwd).first()
        res = {'state_code':1000,'msg':None}
        if user:
            random_str = get_random_str(user.name)
            Token.objects.update_or_create(user=user,defaults={"token":random_str})
            res['token'] = random_str

        else:
            res['state_code'] = 1001 # 错误状态码
            res['msg'] = "用户名或者密码错误"

        return Response(json.dumps(res,ensure_ascii=False))  # 中文转义

import hashlib
import time
def get_random_str(user):
    """md5加密"""
    ctime = str(time.time())
    md5 = hashlib.md5(bytes(user,encoding='utf8'))  # user加盐
    md5.update(bytes(ctime,encoding='utf8'))

    return md5.hexdigest()

5 效果

6.知识点

md5 加盐加密

update_or_create

Token.objects.update_or_create(user=user,defaults={"token":random_str})

if  token没有user,用 create
else   token表 有user 要用update

  中文转义

return Response(json.dumps(res,ensure_ascii=False))  # 中文转义

 2.认证组件 源码剖析

最终效果

 

1 APIView的as_view

2 View的as_view

3 从最上层找dispatch,APIView的dispatch

 

4 reqeust是谁的reqeust

之前构建的新的request

 

 

5 核心代码1

  

tips

7 authenticators怎么来的?传参传进来的

 

8 不定义走默认的

需要在我的类定义这个:

 

9 定义了走我的,如何走

 

 

10 核心代码2

 11 需要在我的类中定义这个,核心代码

我的核心代码源码,没有request,这个怎么来的

 实例对象掉自己的实例方法,有必要传self吗? 不用加

 

 

这里 传了个  形参self  实例对象

slef????   self== 新的request对象

我可以加 request

 

3. 局部视图 

view中添加

from rest_framework import exceptions
class TokenAuth(object):
    def authenticate(self,request):
        token = request.GET.get("token")
        token_obj = Token.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed("验证失败")
        return (token_obj.user.name,token_obj.token)

    def authenticate_header(self,request):  # 暂时不用管
        pass


class BookView(APIView):
    authentication_classes = [TokenAuth,]  # 认证组件
    permission_classes =[]  # 权限组件
    throttle_classes = []  # 频率组件

    def get(self,request):
        book_list = Book.objects.all()
        bs = BookModelSerializers(book_list,many=True,context={'request':request})
        return Response(bs.data)  # Response继承HttpResponse

    def post(self,request):
        # post请求的数据
        bs = BookModelSerializers(data=request.data,context={'request':request})
        if bs.is_valid():
            print(bs.validated_data)
            bs.save()  # create方法
            return Response(bs.data)
        else:
            return Response(bs.errors)

 验证

 

添加token继续验证

 

4. 全局视图认证

这个方法就是被覆盖用的  

 

from rest_framework import exceptions

# 全局视图认证
from rest_framework.authentication import BaseAuthentication

# class TokenAuth(object):
class TokenAuth(BaseAuthentication):
    def authenticate(self,request):
        token = request.GET.get("token")
        token_obj = Token.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed("验证失败")
        return (token_obj.user.name,token_obj.token)

    def authenticate_header(self,request):  # 暂时不用管
        pass


class BookView(APIView):
    authentication_classes = [TokenAuth,]  # 认证组件
    # permission_classes =[]  # 权限组件
    # throttle_classes = []  # 频率组件

    def get(self,request):
        print("request_user",request.user)
        print("request_auth",request.auth)
        book_list = Book.objects.all()
        bs = BookModelSerializers(book_list,many=True,context={'request':request})
        return Response(bs.data)  # Response继承HttpResponse

    def post(self,request):
        # post请求的数据
        bs = BookModelSerializers(data=request.data,context={'request':request})
        if bs.is_valid():
            print(bs.validated_data)
            bs.save()  # create方法
            return Response(bs.data)
        else:
            return Response(bs.errors)

 5. 全局登录认证

 每次取数据都需要 token验证

1.code

utils  认证类

# 全局登录认证
from .models import Token

from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication

class TokenAuth(BaseAuthentication):
    def authenticate(self,request):
        token = request.GET.get("token")
        token_obj = Token.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed("验证失败")
        return (token_obj.user.name,token_obj.token)

    def authenticate_header(self,request):  # 暂时不用管
        pass

 views

author配置自己的,其他配置全局的

from .models import Author

from app01.serilizer import AuthorModelSerializers
from rest_framework import viewsets


class AuthorView(viewsets.ModelViewSet):
    authentication_classes = []   # 加上这个,走自己的认证,也就是不认证
                                  # 不加的话,自己没有,走全局的认证
    # list数据            # create数据              # 继承APIView
    queryset = Author.objects.all()  # queryset,serilizers 名称不能修改
    serializer_class = AuthorModelSerializers

 

settings

STATIC_URL = '/static/'


REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ['app01.utils.TokenAuth']
}

 

2. test

authors走自己写的

 

其他的走全局配置的

 

 3. 源码剖析

 

 

 

auth其实是session

 

 

 

__getattr__方法

getattr是什么,什么时候一定会执行它

attr 就是 DEFAULT_AUTHENTICATION_CLASSES

 

 getattr

 

在再settimg中找

if  settings没有的话,取空字典

 

attr 就是 

DEFAULT_AUTHENTICATION_CLASSES

 

如用我的配置信息,放了我的认证类

走的是dispatch

没有的话,从父类走

 

 4. 我的认证类  核心

用了2个变量,

上面的是 认证类的名字

下面是 类的实例对象

 

 

 

 

3.频率组件

 

posted @ 2019-07-26 15:53  venicid  阅读(297)  评论(0编辑  收藏  举报