视图类(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'})),

    ]


视图类继承关系视图

image

posted @ 2023-04-03 18:35  codegjj  阅读(7)  评论(0编辑  收藏  举报