drf06
内容概要
- 9个视图子类
- 视图集
- 路由系统
- 认证组件
9视图子类
两个视图基类
5个视图扩展类
9个视图子类----》视图类,不需要各位继承
GenericAPIView,只需要继承9个中其中某个,就会有某个或多个接口
五个视图扩展类(不能单独使用)
# 查所有 只有list方法 添加 只有 create方法
from rest_framework.mixins import ListModelMixin, CreateModelMixin
# 查一个只有retrieve方法 修改 update方法 删除 destroy方法
from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin

九个视图子类
# 查所有 get 调用 list post调用 create 请两个结合
from rest_framework.generics import ListAPIView, CreateAPIView, ListCreateAPIView
# 查一个 get 调用 retrieve 修改put 调用 update 删除 delete 调用 destroy
from rest_framework.generics import RetrieveAPIView, UpdateAPIView, DestroyAPIView
from rest_framework.generics import RetrieveUpdateAPIView, RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView


视图集
通过ModelViewSet编写5个接口
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
path('books/<int:pk>/', views.BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
]

from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializer import BookSerializer
class BookView(ModelViewSet):
print(Book.objects.all())
queryset = Book.objects.all()
serializer_class = BookSerializer

serializer层
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name', 'price', 'publish', 'authors',
'publish_dict', 'authors_list']
extra_kwargs = {
'publish': {"write_only": True},
'authors': {"write_only": True},
}
publish_dict = serializers.DictField(read_only=True)
authors_list = serializers.ListField(read_only=True)

通过ReadOnlyModelView编写2个只读的接口
path('books/', views.BookView.as_view({'get': 'list', })),
path('books/<int:pk>/', views.BookView.as_view({'get': 'retrieve', }))
只能写有的没有的写报错
view
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
class BookView(ReadOnlyModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
ViewSetMixin源码分析

魔法方法不限制于函数,还有可能是类


路由匹配成功走ViewSetMixin的as_view,修改请求于函数的对应关系,然后去除django原生的csrf校验
总结
-
只要继承了ViewSetMixin的视图类,路由写法就变了(ViewSetMixin重写了as_view)
-
变成需要传入字典:
{"get": "list"}只要传入
actions,以后访问get就是访问list。 -
其他执行跟之前一样
-
以后视图类,类中的方法名,可以任意命名,只要在路由中做好映射即可【重要】
from rest_framework.viewsets包下的类
# 魔法,继承了修改路由的写法 一定要写继承父类的第一个
from rest_framework.viewsets import ViewSetMixin
# APIView+ViewSetMixin GenericAPIView + ViewSet
from rest_framework.viewsets import ViewSet, GenericViewSet
#
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet

重点
以后,你想继承APIView,但是想修改路由写法[视图类中的自定义命名],可以继承ViewSet。
以后,需要跟数据库打交道,想修改路由写法可以继承GenericViewSet。
视图层大总结
-
两个视图基类
APIView,GenericAPIView
-
5个视图扩展类,不是视图类,必须配合GenericAPIView
-
9个视图子类,是视图类,只需继承其中一个就可以了
-
视图集
-ModelViewSet:路由写法变了,只需写两行,5个接 口都有了
-ReadOnlyModelViewSet:路由写法变了,只需要写 两行,2个只读接口都有了
-ViewSetMixin:不是视图类,魔法,重写了
as_view,路由写法发送改变,变成了映射views.BookView.as_view({'get': 'list', 'post': 'create'})-ViewSet:ViewSetMixin + APIView
-GenericViewSet : GenericAPIView + ViewSetMixin
ModelViewSet::五个扩展类+GenericViewSet
路由系统
def 由于继承ViewSetMixin类,路由写法变了
原生 + drf,以后的路由写法,可能会有如下情况(三种情况)
1. path("books/",view.BookView.as_view())
2. path("books/",view.BookView.as_view({"get": "list"}))
3. 自动生成
drf提供了两个路由类,继承ModelViewSet后,路由可以自动生成
使用步骤:
-
导入路由类
from rest_framework.routers import SimpleRouter, DefaultRouter -
生成实例化对象
router = SimpleRouter() 或 router = DefaultRouter() -
注册
第一个参数是路由 不要加/ 第二个参数是视图类 第三个参数是别名,一般和路由相同 router.register('books', views.BookView, 'books') -
在urlpatterns中注册,两种方式
-
urlpatterns += router.urls -
path('api/v1/', include(router.urls))
-


底层实现:自动生成路由
-本质就是自动做映射,能够自动生成的前提是,视图类中要有 5个方法的某一个或多个
get --------> list
post --------> create
get --------> retrieve
put --------> update
delete -------> destory
ModelViewSet,ReadOnlyModelViewSet可以自动生成
9个视图子类 + 配合ViewSetMixin 才可以自动生成
GenericAPIView + 5个视图扩展类 + 配合ViewSetMixin 才能自动生成
actionc装饰器
actionc 写在视图类的方法上,可以自动生成
url层
from rest_framework.routers import SimpleRouter, DefaultRouter
router = SimpleRouter()
router.register('test', views.SendView, 'test')
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
view
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from rest_framework.decorators import action
class SendView(ViewSet):
# 请求方式
#是否需要pk
#会在路由后在拼接一个 url_path 可以不写
# url_name:别名,反向解析使用,了解即可
@action(methods=['POST', ], detail=False, url_path='send')
def send(self, request):
print(request.query_params)
return Response("我是send")

补充
SimpleRouter和DefaultRouter就是一个路由多一个路由少。我们一般用SimpleRouter,因为少,虽然少但是跟DefaultRouter功能实现的差不多
-
不同的请求方式可以使用不同序列化类
-
不同action使用不同序列化类
class SendView(ViewSet): def get_serializer(self): # self.action 拿到的 方法的名字 if self.action == "lqz": return LqzSerializer elif self.action == "json": return JsonSerializer elif self.action == "send": return BookSerializer
认证组件
访问某个接口,需要登录后才能访问
第一步:登录功能,和用户表
User表
UserToken表:存储用户登录状态
登录接口
#### 表模型
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
class UserToken(models.Model): # 跟User是一对一
token = models.CharField(max_length=32)
user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True)
# user :反向,表名小写,所有有user字段
### 路由
router.register('user', views.UserView, 'user') # /api/v1/user/login post 请求
# 视图类
#### 登录接口 自动生成路由+由于登录功能,不用序列化,继承ViewSet
from .models import User, UserToken
import uuid
class UserView(ViewSet):
@action(methods=['POST'], detail=False)
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username, password=password).first()
if user:
# 用户存在,登录成功
# 生成一个随机字符串--uuid
token = str(uuid.uuid4()) # 生成一个永不重复的随机字符串
# 在userToken表中存储一下:1 从来没有登录过,插入一条, 2 登录过,修改记录
# 如果有就修改,如果没有就新增 (if 自己写)
# kwargs 传入的东西查找,能找到,使用defaults的更新,否则新增一条
UserToken.objects.update_or_create(user=user, defaults={'token': token})
return Response({'code': '100', 'msg': '登录成功', 'token': token})
else:
return Response({'code': '101', 'msg': '用户名或密码错误'})
作业
# 1 整理出视图层所有的类,以及继承关系,最好画图,每个视图类,扩展类,如何使用,也要简单写写
# 2 整理自动生成路由和装饰器
# 3 登录接口写完(if,我写的方式)
---扩展
# 3 登录认证功能,没写,登录功能写完了
-实现了图书5个接口
-图书的查询所有和新增不需要登录访问
-图书的查询一个,修改,删除,登录访问
view层
from .models import Book
from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
import uuid
from .models import User, UserToken
from rest_framework.response import Response
import hashlib
def get_md5(data):
md5 = hashlib.md5()
md5.update("宝塔镇河妖".encode("utf8"))
md5.update(str(data).encode("utf8"))
md5.update("天王盖地虎".encode("utf8"))
return md5.hexdigest()
def outer(func):
def inner(request, *args, **kwargs):
token = request.session.get("token")
print(token)
if token:
user_id = UserToken.objects.filter(token=token).first().user_id
request.user_id = user_id
res = func(request, *args, **kwargs)
return res
else:
return Response({"msg": "滚蛋"})
return inner
from django.utils.decorators import method_decorator
from .serializer import BookSerializer
from rest_framework.viewsets import ModelViewSet
class UserView(ViewSet):
@action(methods=['POST', ], detail=False)
def login(self, request):
username = request.data.get("username")
password = request.data.get("password")
res = User.objects.filter(username=username, password=password).first()
if res:
# 生成一个独一无二
token = uuid.uuid4()
request.session['token'] = "%s|%s" % (str(token), str(get_md5(res.pk)))
UserToken.objects.update_or_create(user=res, defaults={"token": request.session["token"]})
return Response({'code': 100, 'msg': "登录成功"})
else:
return Response({'code': 101, 'msg': "用户名或密码错误"})
@method_decorator(outer)
@action(methods=["DELETE", ], detail=False)
def logout(self, request):
request.session.delete()
return Response()
class BooksView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
@method_decorator(outer)
@action(methods=['POST'], detail=False)
def create_book(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
@method_decorator(outer)
@action(methods=['PUT'], detail=True)
def update_book(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
@method_decorator(outer)
@action(methods=['DELETE'], detail=True)
def delete_book(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
serializer层
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name', 'price', 'publish', 'authors',
'publish_dict', 'authors_list']
extra_kwargs = {
'publish': {"write_only": True},
'authors': {"write_only": True},
}
publish_dict = serializers.DictField(read_only=True)
authors_list = serializers.ListField(read_only=True)
model层
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name="书名")
price = models.CharField(max_length=12, verbose_name="价格")
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
@property
def publish_dict(self):
return {"name": self.publish.name, "addr": self.publish.addr}
@property
def authors_list(self):
l = [{"name": author_obj.name, "gender": author_obj.gender.get_gender_display()} for author_obj in
self.authors.all()]
return l
class Meta:
verbose_name_plural = '图书表'
def __str__(self):
return "图书对象%s" % self.name
class Publish(models.Model):
name = models.CharField(max_length=32, verbose_name='出版社名称')
addr = models.CharField(max_length=12, verbose_name='出版社地址')
class Meta:
verbose_name_plural = '出版社表'
def __str__(self):
return '出版社对象%s' % self.name
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name='作者名称')
gender = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
class Meta:
verbose_name_plural = '作者表'
def __str__(self):
return '作者对象%s' % self.name
class AuthorDetail(models.Model):
choice_dict = ((1, '男'), (2, '女'))
gender = models.IntegerField(choices=choice_dict, verbose_name='性别')
class Meta:
verbose_name_plural = '作者详情表'
class User(models.Model):
username = models.CharField(max_length=32, verbose_name="用户名")
password = models.CharField(max_length=32, verbose_name="密码")
class UserToken(models.Model):
token = models.CharField(max_length=32, verbose_name="token")
user = models.OneToOneField(to="User", on_delete=models.CASCADE)

浙公网安备 33010602011771号