视图集ModelViewSet
ModelViewSet
-视图类:GenericAPIView(继承)
- 路由映射:list create retrieve update destroy
继承5个视图扩展类:
CreateModelMixin,ListModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin
-路由写法改变:ViewSetMixin 只要继承它,路由写法就的改变
-分析:ViewSetMixin 不是视图类,支持路由映射的写法,核心原理是重写了as_view
-请求来了
原来:执行APIView--》as_visw内的view(request)
执行 ViewSetMixin的---》as_view内的 view(request)
源码分析:as_view
def as_view(cls, actions=None, **initkwargs):
# actions={'get': 'list', 'post': 'create'}
def view(request, *args, **kwargs):
self = cls(**initkwargs)#self是BookView视图类的对象
self.action_map = actions#actions这个字典
for method, action in actions.items():
#method:get action:list
#去实体类的对象中 self中 反射list的方法
#handler就是BookView(父类)中的list方法
handler = getattr(self, action)#反射action(list),反射设置值--》把method:get,设置成了list
#BookView类的对象,以后get方法就是list方法
setattr(self, method, handler)
# 根据请求方式执行跟请求方式同名的方法 get请求---》get方法---》list
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
-ViewSetMixin的总结--》只要继承它
-以后路由写法as_view必须传字典---》映射关系
-什么请求方式---》执行视图类中什么方法 的映射关系
#ViewSetMixin必须写在视图类前
#2 ViewSetMixin类--》路由写法变了的
#3 ViewSet:以后想继承APIView,但是路由写变了,就继承它
#4 GenericViewSet:以后想继承GenericViewSet,但是路由写变了,就继承它
#5 ReadOnlyModelViewSet:只读
-视图类:GenericAPIView
- 映射:list retrieve :只查询,查询所有和查询单条ListModelMixin,RetrieveModelMixin
-路由写法变了:ViewSetMixin
# 视图类,继承ReadOnlyModelViewSet,不能多写,写多了会报错
# path('books/', views.BookView.as_view({'get': 'list'})),
# path('books/<int:id>', views.BookView.as_view({'get': 'retrieve'})),
视图层(小概括):
# 视图层:
# 两个视图基类
APIView
GenericAPIView
# 5个视图扩展类---》不是视图类,必须结合GenericAPIView
CreateModelMixin:create---》原来咱们post中得代码,新增
ListModelMixin:list---》原来获取所有
RetrieveModelMixin:retrieve---》原来获取单条
UpdateModelMixin:update--->修改
DestroyModelMixin:destroy--》删除
# 9个视图子类
CreateAPIView
ListAPIView
RetrieveAPIView
DestroyAPIView
UpdateAPIView
ListCreateAPIView
RetrieveUpdateDestroyAPIView
RetrieveDestroyAPIView
RetrieveUpdateAPIView
# 视图集:
ModelViewSet:所有
ReadOnlyModelViewSet:只读
ViewSetMixin:路由写法变了
ViewSet:ViewSetMixin+APIView
GenericViewSet:ViewSetMixin+GenericAPIView
### 现在有很多视图类,不知道选哪个,具体实战
# 1 publish的5个接口--ModelViewset
# 2 publish的5个接口,获取所有,带code和msg--ModelViewset--》重写list
# def list(self, request, *args, **kwargs):
# res = super().list(request, *args, **kwargs)
# return Response({'code': 100, 'msg': '查询所有成功', 'result': res.data})
# 3 写 查询所有,修改一条,路由写法变了
# ViewSetMixin,GenericAPIView
#ListModelMixin,UpdateModelMixin
# from rest_framework.mixins import ListModelMixin,UpdateModelMixin
# class BookView(ViewSetMixin,GenericAPIView,ListModelMixin,UpdateModelMixin):
# queryset = Book.objects.all()
# serializer_class = BookSerializer
#路由
# path('books/', views.BookView.as_view({'get': 'list'})),
# path('books/<int:id>', views.BookView.as_view({'put': 'update'})),
# 4 用户视图类,有登录(login)和注册(register)接口--->第一个跟数据库打交道,第二 做序列化
# class UserView(ViewSet):
# def login(self):
# pass
# def register(self):
# pass
# path('login/', views.UserView.as_view({'post': 'login'})),
# path('register/', views.BookView.as_view({'post': 'register'})),
# 5 新增一条图书,路由写法变了
# from rest_framework.generics import CreateAPIView
# class BooView(ViewSetMixin,CreateAPIView):
# queryset = None
# serializer_class = None
# #path('books/', views.UserView.as_view({'post': 'create'})),
# 6 查询一条和删除
from rest_framework.generics import RetrieveDestroyAPIView
# class BooView(RetrieveDestroyAPIView):
# class BooView(GenericAPIView,RetrieveModelMixin,DestroyModelMixin):
# queryset = None
# serializer_class = None
drf之路由
# 0 视图类没有继承了ViewSetMixin,路由写法跟之前一样
path('books/', views.BookView.as_view())
# 1 只要视图类继承了ViewSetMixin,路由写法必须写成映射的方式
path('books/', views.BookView.as_view({'get': 'list'})),
只要视图类继承了ModelViewSet,还可以这么写
#1 导入
from rest_framework.routers import SimpleRouter
#2 实例化
router = SimpleRouter()
#3 注册路径
router.register('books', views.BookView, 'books')
#4 加入到路由中
urlpatterns += router.urls
# 5 list,create,retrieve,destroy,update--->自动映射--》SimpleRouter
假设视图类中有个login,如何做对应?
from rest_framework.decorators import action
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# list,create,retrieve,destroy,update--->自动映射--》SimpleRouter
# 手动映射
#methods=None, 请求方式
# detail=None, 只能写True或False,如果写了false就是不带pk的路径,如果写了True路径带pk
# False的情况: http://127.0.0.1:8080/api/v1/books/login/
# True 的情况: http://127.0.0.1:8080/api/v1/books/8/login/
# url_path='login' 路径,会在之前的路径上,拼这个路径,如果不写默认以函数名拼接
# - http://127.0.0.1:8080/api/v1/books/login/
# url_name=None :别名,用做反向解析
@action(methods=['POST'],detail=False,)
def login(self,request):
return Response('login')
总结:以后只要继承ViewSetMixin,就可以使用SimpleRouter方式写路由
#1 导入
from rest_framework.routers import SimpleRouter,DefaultRouter
#2 实例化 :SimpleRouter,DefaultRouter
router = SimpleRouter()
或:认为他们一样即可---》DefaultRouter多一条路径
router = DefaultRouter()
#3 注册路径
router.register('books', views.BookView, 'books')
#4 加入到路由中:
# 方式一:(用这个)
urlpatterns += router.urls
# 方式二:
urlpatterns = [
path('', include(router.urls)),
]
# 5 list,create,retrieve,destroy,update--->自动映射--》SimpleRouter
# 6 视图类中自己的方法,再做映射--action装饰器
@action(methods=['POST'],detail=False,)
def login(self,request):
return Response('login')
认证组件
认证---》登录认证
-登录进系统后,以后再访问接口,需要携带登录信息,如果没携带,不允许方法---》这个控制就是认证
-cookie(客户端浏览器上)和session(后端存储的键值对)
模块层
#### 用户表,用来做登录
### 用户登录成功户,存登录状态
class User(models.Model):
name = models.CharField(max_length=64)
password = models.CharField(max_length=64)
gender = models.CharField(max_length=64, default="男")
# user_token = models.OneToOneField(to='UserToken')
class UserToken(models.Model):
user = models.OneToOneField(to='User', on_delete=models.CASCADE)
token = models.CharField(max_length=64)
视图层
import uuid
from .auth import LoginAuth
class BookView(ReadOnlyModelViewSet): # list retrieve
authentication_classes = [LoginAuth] # 5个接口必须登录才能呢访问
# 配置两个类属性
queryset = Book.objects.all()
serializer_class = BookSerializer
def list(self, request, *args, **kwargs):
res = super().list(request, *args, **kwargs)
return Response({'code': 100, 'msg': '查询所有成功', 'result': res.data})
from .models import User, UserToken
class UserView(ViewSet):
@action(methods=['POST'],detail=False)
def login(self,request):
# 前端传入的username和password request.data
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(name=username, password=password).first()
if user:
# 生成 随机字符串,放到UserToken表中,把随机字符串返回给前端
token = str(uuid.uuid4())
# 如果之前UserToken表中有记录,就要更新,如果没有记录,就要新增
# 先去UserToken查,如果查到,更新 update更新,查不到UserToken.objects.crete()
# 有就更新,没有就新增
# 通过user_id=user.pk去查询,能查到,使用defaults中得值更新,更新token
# 如果查不到,使用 通过user_id=user.pk,token=token 新增一条
UserToken.objects.update_or_create(defaults={'token': token}, user_id=user.pk)
return Response({'code': '100', 'msg': '登录成功', 'token': token})
else:
return Response({'code': '101', 'msg': '用户名或密码错误'})