视图类(Views)
顶级封装 ModelViewSet
1.五接口实现 增删改查,一应俱全
具体见 2 3 4 中的代码:
http://127.0.0.1:8001/book/books/
# 查询全部
get: list
# 创建 1 个 (必须是单个字典类型)
post: create
"""
下列三个需要传对应的id
"""
# http://127.0.0.1:8001/book/books/4
get: retrieve # 单个查询
put: update # 单个修改
delete: destroy # 单个删除
2. views.py
from rest_framework.viewsets import ModelViewSet
from books.models import Book
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSer
3. urls.py
得益于继承了 ViewSetMixin (改变了路由的写法)
3.1.方式一:
"""----------urls.py----------"""
from rest_framework.routers import SimpleRouter
from books.views import BookView
router = SimpleRouter()
router.register('books', BookView, 'books')
urlpatterns = [
]
urlpatterns += router.urls
"""----------views.py----------"""
from rest_framework.viewsets import ModelViewSet
from books.models import Book
class BookView(ViewSetMixin, ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSer
# 这里需要使用 action 进行自定义路径 url.py 的路径加 url_path
# eg: http://127.0.0.1:8001/user/user/login1/
@action(methods=['post'], detail=False, url_path='login1', url_name='login')
def post(self, request):
pass
3.2.方式二:
"""----------urls.py----------"""
from django.urls import path
from books.views import BookView
urlpatterns = [
# 不同请求类型对应的不同方法 《里面的 value 可以是自定义值》
# eg: {'get': 'aaaa', 'post': 'bbbbb'}) 如: 《下面的自定义》
path('books/', BookView.as_view({'get': 'list', 'post': 'create'})),
path('books/<int:pk>', BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]
"""----------views.py----------"""
from rest_framework.viewsets import ModelViewSet
from books.models import Book
class BookView(ViewSetMixin, ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSer
# 自定义
def aaaa(self, request):
pass
APIView基本使用
1.继承
1. 如果使用了drf,以后写的都是cbv,但是都是继承drf提供的视图类,APIView。
2. 继承了 ApiView 所有视图类都没有csrf 认证了, 就不需要注释掉全局的 csrf 中间件。
from rest_framework.views import APIView
2.注册(rest_framework)
Drf是个app,需要注册 (setting.py中注册)
INSTALLED_APPS = [
...
'rest_framework' # 一定不要忘了注册
]
3.取值方式 《POST请求为例》(重点)
3.1.原生 Django
请求方式 | 前端提交的数据类型 | 后端取值方式 | 提示 |
---|---|---|---|
POST | urlencode | request.POST | |
POST | formdata | request.POST | |
POST | json编码 | request.body | 需要进行 json 转化 |
POST | 文件 | request.FILES | |
《PUT》 | 文件 | request.body | 需要自己处理 |
3.2. 用 Drf 视图类中继承 ApiView
重写了 request 方法,多了一个 data 属性, 其他东西一样。
前端提交的数据类型 | 后端取值方式 |
---|---|
urlencode | request.data |
formdata | request.data |
json编码 | request.data |
文件 | request.FILES |
4.取值拦截
无论什么格式编码传入的body数据,都是从request.data中取。
(1)利用装饰器
import json
def add_data(func):
def inner(request,*args, **kwargs):
try:
request.data = json.loads(request.body)
except Exception as e:
request.data = request.POST
res = func(request,*args, **kwargs)
return res
return inner
(2)重写dispatch方法
from django.views import View
class MyView(view):
def dispatch(self, request, *args, **kwargs)
try:
request.data = json.loads(request.body)
except Exception as e:
request.data = request.POST
return super().dispatch(request, *args, **kwargs)
使用APIView+序列化类(删查) 反序列化(增改)
1. views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer
# 图书新增:psot 图书查询所有:get
class BookView(APIView):
def get(self, request):
book_list = Book.objects.all()
ser=BookSerializer(instance=book_list,many=True)
return Response(ser.data)
def post(self,request):
# 反序列化--->传得是data=前端传入的数据request.data
ser=BookSerializer(data=request.data)
# 数据校验
if ser.is_valid(): # forms组件就这么做的
# 保存-->会报错---->需要在序列化类中重写create方法
ser.save()
return Response({'msg':'新增成功','code':100})
else:
print(ser.errors)
return Response({'msg': '数据没有校验通过', 'code': 101})
# 图书查询一个:get ,图书修改一个:put ,图书删除一个:delete
class BookDetailView(APIView):
def get(self,request,pk):
book = Book.objects.all().filter(pk=pk).first()
ser = BookSerializer(instance=book)
return Response(ser.data)
def delete(self,request,pk):
Book.objects.all().filter(pk=pk).delete()
return Response({'code':100,'msg':"删除成功"})
def put(self,request,pk):
# 拿到要修改的对象. first() 取第一个,不然报错。
book=Book.objects.filter(pk=pk).first()
# 使用data的数据,修改book这个对象
ser = BookSerializer(instance=book,data=request.data)
if ser.is_valid():
# 会报错,需要重写序列化类的updata方法
ser.save()
return Response({'code':100,'msg':"修改成功"})
else:
return Response({'code': 101, 'msg': "修改失败"})
2. serializers.py
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.Serializer):
# read_only=True 意思是只读,只用来做序列化,不用来做反序列化
# 如果不写read_only=True,这个字段必传,如果不传,数据校验不过
# 如果写了read_only=True,这个字段不传
id=serializers.IntegerField(read_only=True) # model中Auto,本质就是数字,IntegerField
name=serializers.CharField(max_length=8,min_length=3,error_messages={'max_length':'太长了'})
# price=serializers.IntegerField()
price=serializers.CharField(max_length=1000) # 写成CharField也能映射成功
def create(self, validated_data): # 代码一点没少写,甚至多了,好处解耦了,view代码少了
# validated_data就是校验过后的数据
# 高级
book=Book.objects.create(**validated_data)
# 菜鸡
# name=validated_data.get('name')
# price=validated_data.get('price')
# book = Book.objects.create(name=name,price=price)
return book # 一定要return新增的对象
def update(self, instance, validated_data):
# instance 是要修改的对象
# validated_data是校验过后数据
instance.name=validated_data.get('name')
instance.price=validated_data.get('price')
instance.save() # 一定不要忘了保存,才存到数据库
return instance # 一定要return新增的对象
GenericAPIView基本使用
1. 重要属性及方法
重要的类属性
# 要序列化的数据
queryset = Book.objects.all()
# 序列化类
serializer_class = BookSerializer
重要的方法
# 获取单个对象(不需要传递 id 号, 源码会根据 lookup_field = 'pk',进行查找。)
self.get_object()
# 获取多个
self.get_queryset()
# 序列化类
self.get_serializer(instance=book_list, many=True)
2. 代码案例
(1). views.py
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from .serialize import BookSerializers
from .models import Book
class BookView(GenericAPIView):
# 类属性
queryset = Book.objects.all()
serializer_class = BookSerializers
def get(self, request):
book = self.get_queryset()
res = self.get_serializer(instance=book, many=True)
return Response(res.data)
def post(self, request):
ser = self.get_serializer(data=request.data)
if ser.is_valid():
ser.save()
return Response({'msg': '新增成功', 'code': 100, 'result': ser.data})
else:
return Response({'msg': '数据没有校验通过', 'code': 101, 'result': ser.errors})
class BookDetailView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializers
def get(self, request, pk):
book = self.get_object()
res = self.get_serializer(instance=book)
return Response(res.data)
def put(self, request, pk):
book = self.get_object()
res = self.get_serializer(instance=book, data=request.data)
if res.is_valid():
res.save()
return Response({'code': 200, 'msg': '修改成功!', 'result': res.data})
else:
return Response({'code': 200, 'msg': '修改失败!', 'result': [res.errors, res.error_messages]})
(2). serializers.py
from rest_framework import serializers
from .models import Book
class BookSerializers(serializers.ModelSerializer):
# 重写了 author_id 字段
author_id = serializers.IntegerField(write_only=True)
class Meta:
model = Book
depth = 2
fields = ['name', 'publish', 'author_id', 'Author_detail']
extra_kwargs = {
'name': {'max_length': 8, 'min_length': 3},
'publish': {'max_length': 15, 'min_length': 3},
'author_detail': {'read_only': True}
}
Author_detail = serializers.SerializerMethodField()
def get_Author_detail(self, obj):
return {'id': obj.author.pk, 'name': obj.author.name, 'age': obj.author.age}
(3). models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=60)
publish = models.CharField(max_length=60)
author = models.ForeignKey(to='Author', on_delete=models.CASCADE)
class Author(models.Model):
name = models.CharField(max_length=60)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE, default=1)
五个扩展类
1. 引言(自己封装)
这里是自己封装的, 其实别人已经封装好了, 具体往下看。
自己封装 5 接口 (点击查看)
"""导入GenericAPIView类"""
from rest_framework.generics import GenericAPIView
# 查询所有对象
class GetsModelMixin:
def get(self, request):
obj = self.get_queryset()
res = self.get_serializer(instance=obj, many=True)
print(res.data)
return Response(res.data)
# 增加对象
class PostModelMixin:
def post(self, request):
res = self.get_serializer(data=request.data)
if res.is_valid():
res.save()
return Response({"msg": "新增成功!", "code": 100})
else:
return Response({"msg": "新增失败!", "code": 101, "err": res.errors})
# 查询单个对象
class GetModelMixin:
def get(self, request, *args, **kwargs):
obj = self.get_object()
res = self.get_serializer(instance=obj)
print(res.data)
return Response(res.data)
# 修改单个对象
class PutModelMixin:
def put(self, request, *args, **kwargs):
obj = self.get_object()
res = self.get_serializer(instance=obj, data=request.data)
if res.is_valid():
res.save()
return Response({"msg": "修改成功!", "code": 100})
else:
return Response({"msg": "修改失败!", "code": 101, "err": res.errors})
# 删除单个对象
class DeleteModelMixin:
def delete(self, request, pk):
self.get_object().delete()
return Response({"msg": "删除成功!", "code": 100})
# book表 查增
class BookView(GenericAPIView, GetsModelMixin, PostModelMixin):
queryset = models.Book.objects
serializer_class = serialize.MyBookSerialize
def get(self, request):
return super().get(request)
def post(self, request):
return super().post(request)
# book表 查改删
class BookDetailView(GenericAPIView, GetModelMixin, PutModelMixin, DeleteModelMixin):
queryset = models.Book.objects.all()
serializer_class = serialize.MyBookSerialize
def get(self, request, *args, **kwargs):
return super().get(request, kwargs.get('pk'))
def put(self, request, *args, **kwargs):
return super().put(request, kwargs.get('pk'))
def delete(self, request, *args, **kwargs):
return super().delete(request, kwargs.get('pk'))
2. 源码封装
这里是源码封装的
(1). 五个扩展类介绍
"""
视图扩展类 ---> 不是视图类 ---> 没有父类,
只是对一些重复的代码做了封装, 真正处理业务还是 GenericAPIView。
"""
# 获取所有
ListModelMixin + GenericAPIView
# 创建一个
CreateModelMixin + GenericAPIView
# 查询一个
RetrieveModelMixin + GenericAPIView
# 更新一个
UpdateModelMixin + GenericAPIView
# 删除一个
DestroyModelMixin + GenericAPIView
(2). 代码展示
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
# book表 查增
class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = models.Book.objects
serializer_class = serialize.MyBookSerialize
# 有参数是这么写,下面亦是如此。
def get(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
def post(self, request):
return super().create(request)
# book表 查改删
class BookDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = models.Book.objects.all()
serializer_class = serialize.MyBookSerialize
def get(self, request, *args, **kwargs):
return super().retrieve(request, kwargs.get('pk'))
def put(self, request, *args, **kwargs):
return super().update(request, kwargs.get('pk'))
def delete(self, request, *args, **kwargs):
return super().destroy(request, kwargs.get('pk'))
九个视图子类
(1). 九个试图子类介绍
# 获取所有
ListAPIView
# 新增一个
CreateAPIView
# 获取单个
RetrieveAPIView
# 更新一个
UpdateAPIView
# 删除一个
DestroyAPIView
"""---------------------结合类-------------------"""
# 获取所有和新建一个
ListCreateAPIView
# 获取一个和更新一个和删除一个
RetrieveUpdateDestroyAPIView
# 获取一个和更新一个
RetrieveUpdateAPIView,
# 获取一个和删除一个
RetrieveDestroyAPIView
(2). 代码展示
继承9个视图子类后,只需要在视图类中写两个类属性即可,不需要在写对应的请求方法了。
"""导入类"""
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
class BookView(ListCreateAPIView): # 获取所有和新增一个
queryset = models.Book.objects # 要序列化的数据
serializer_class = serialize.MyBookSerialize # 序列化类
class BookDetailView(RetrieveUpdateDestroyAPIView): # 获取一个和更新一个和删除一个
queryset = models.Book.objects.all() # 要序列化的数据
serializer_class = serialize.MyBookSerialize # 序列化类
ModelViewSet
视图集 ModelViewSet 继承了五个扩展类和一个视图基类
(1). 源码展示
# ModelViewSet 的继承关系
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
(2). 导入方式
from rest_framework.viewsets import ModelViewSet
(3).代码展示
路由层(urls.py)
path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
path('books/<int:pk>', views.BookView.as_view({'get': 'retrieve', 'put':'update', 'delete': 'destroy'})),
视图层(models.py)
from rest_framework.viewsets import ModelViewSet
# 此时所有的请求都在一个视图类中, 需要做路由映射(具体看路由中的代码)。
# 上面的一些视图类写法也可以做路由映射。
class BookView(ModelViewSet):
queryset = models.Book.objects
serializer_class = serialize.MyBookSerialize
ViewSetMixin (改变路由写法)
以后你想继承APIView,但是路由想自动生成,继承ViewSet
以后想继承GenericAPIView,但是路由想自动生成,继承GenericViewSet
1. 关系总结
""" 以下几个都继承了 ViewSetMixin, 路由写法需改改变。 """
# 继承了所有的视图及扩展类
ModelViewSet
# 只读 (查询单个 和 查询所有)
# RetrieveModelMixin + mixins.ListModelMixin + GenericViewSet(ViewSetMixin + GenericAPIView)
ReadOnlyModelViewSet
# 继承了 ViewSetMixin + APIView
ViewSet
# 继承了 ViewSetMixin + GenericAPIView
GenericViewSet
2. 代码案列
eg: 代码(1)是登录请求 (但是字面意义看不出是什么请求, 此时想把 post --改为--> login)
那么,就需要继承 ViewSetMixin 对路由进行代码改造(如代码(2))。
# 代码(1)
class BookView(GenericAPIView):
def post(self, request):
pass
# 代码(2)
# views.py
# 1. 这里继承也变了哦
class BookView(ViewSetMixin, GenericAPIView):
# 2. 这里把方法名改为 login
def login(self, request):
pass
# url.py
urlpatterns = [
# 3. 这里做路由映射就可以了
path('book/', BookView.as_view({'post': 'login'})),
]