rest_framework视图三部曲、认证组件
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
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()), ]
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
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))
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个变量,
上面的是 认证类的名字
下面是 类的实例对象




浙公网安备 33010602011771号