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
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个变量,
上面的是 认证类的名字
下面是 类的实例对象
3.频率组件