Day 24 DRF视图集

Day 24 DRF视图集

一、DRF-ModelSerializer多对多数据创建

models.py

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    author = models.ManyToManyField(to='Author', null=True)  # 多对多关系
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)  # 外键关系


class Publish(models.Model):
    name = models.CharField(max_length=32)
    address = models.CharField(max_length=64)


class Author(models.Model):
    name = models.CharField(max_length=32)
    salary = models.IntegerField()
    phone = models.CharField(max_length=11)

serializer.py

class Author(serializers.ModelSerializer):
    class Meta:
        # 关联
        model = models.Author
        # 选取需要显示的字段
        fields = '__all__'


class Publish(serializers.ModelSerializer):
    class Meta:
        model = models.Publish
        fields = ['name', 'address']


class BookModel(serializers.ModelSerializer):
    author1 = Author(read_only=True)
    publish = Publish()  # 外键这样能够读出来但是不能存 
    # 存
    # publish1 = Publish(read_only=True)
    
    class Meta:
        model = models.Book  # 表模型 与之关联
        fields = '__all__'

image-20201106162231549

但是换一种是不行的

没有read_only = True

class BookModel(serializers.ModelSerializer):
    author = Author()
    publish = Publish()

这样是不行的,因为两个字段对应的是表对象,

get的时候,由于原本 author 对应的是关系,现在变成了表对象(覆盖了) publish 也是一样所以也是查不到数据的

image-20201106163137040

post的时候默认都是要存入完整的对象的 也就是一个字典

image-20201106163002535

出现这个问题的原因是由于多对多的关系很特殊,他是有一张 中间表 的 把原来对应的 表关系 换成 表对象 就找不到对应字段的数据,所以就报错,这里我们就要保留 原来的 表关系(author字段)

如果我们要跨表查询 就需要重新 定义一个字段名(author1),并将其设置为 read_only属性 让drf通过 ModelSerializer 寻找对应的字段内容,如果不设置 就会与自己的(author) 发生冲突,也就是 读写 分离, 自己的负责 write_only,这样就能正常读取和写入了!

二、视图基类(2个)

Django REST framwork 提供视图的主要作用

  • 控制序列化器的执行(检验,保存,转换数据)
  • 控制数据库的查询

这两个最大的区别是 GenericAPIView继承APIView类的,在拥有APIView所有特性外,还提供了链接Model类与Serializer类的接口

扫描二维码关注公众号,回复: 12218661 查看本文章

1. APIView

是REST framework提供的所有视图的基类,继承了原生Django的View

从之前的流程中我们知道,他是继承父类的as_view方法,执行自己的dispath,然后返回返回REST framework的Response对象,当然这其中还发生了很多事情!

在APIView仍然以常规的类视图定义方法来实现get(),post()或着其他请求方法。

2.GenericAPIView

继承自APIVIew主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可搭配一个或多个Mixin扩展类。

2.1支持定义的属性

  • 列表视图与详情视图通用
    • queryset 列表视图的查询集
    • serializer_class 视图使用的序列化器
  • 列表视图使用
    • pagination_class 分页控制类
    • filter_bankends 过滤控制后端
  • 详情页视图使用
    • lookup_field 查询单一数据库对象时使用的条件字段,默认为“pk”
    • lookup_url_kwarg 查询单一数据时URL中的参数关键字名字,默认与look_field相同

2.2 提供的方法

  • 列表视图与详情视图
    • get_queryset() 返回视图使用的查询集,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写
    • get_serializer_class() 返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法
    • get_serializer() 返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法。
    • get_object() 返回详情视图需要的一个模型对象,默认使用lookup_field参数来过滤queryset

我们来试一下GenericAPIView

serializer.py同上

views.py

from rest_framework.generics import GenericAPIView
from rest_framework.response import Response

class Book(GenericAPIView):
    # 因为是queryset对象 可以自调用 加不加all()不重要,源码中默认加了 all()
    queryset = models.Book.objects
    serializer_class = serializer.BookModel
    
    def get(self, request, *args, **kwargs):
        books = self.get_queryset()
        ser = self.get_serializer(books, many=True)
        return Response(ser.data)
    
    def post(self, request, *args, **kwargs):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

我们来发送下get请求,得到了我们需要的数据image-20201106194512105

我们来研究下源码

image-20201106202345992

单个记录的 查改删的操作

urls.py
url(r'^bookinfo/(?P<pk>\d+)', views.BookInfo.as_view()),
views.py

整体逻辑和我们之前APIView差不多,方法上有略微改变

class BookInfo(GenericAPIView):
    queryset = models.Book.objects
    serializer_class = serializer.BookModel
    
    def get(self,request,*args, **kwargs):
        book = self.get_object()
        ser = self.get_serializer(book)
        return Response(ser.data)
    
    def patch(self,request,*args, **kwargs):
        book = self.get_object()
        ser = self.get_serializer(book, data = request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return  Response('保存失败!')
        
    def delete(self,request,*args, **kwargs):
        book = self.get_object().delete()
        return Response('删除成功!')

这次只测试下 修改方法

image-20201106203733877

源码分析

image-20201106210552150

三、视图扩展类(5个)

提供了几种后端视图(对数据资源进行曾删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。

这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器数据库查询的方法。

1. ListModelMixin

列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。该Mixin的list方法会对数据进行过滤分页

源代码:

Copyclass ListModelMixin(object):
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        # 过滤
        queryset = self.filter_queryset(self.get_queryset())
        # 分页
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        # 序列化 自己覆盖GenericAPIView的queryset和serializer
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

举个栗子

class Book(ListModelMixin, CreateModelMixin, GenericAPIView):
    
    queryset = models.Book.objects
    serializer_class = serializer.BookModel
    
    def get(self, request, *args, **kwargs):
        # 调用继承的ListModelMixin的list方法并返回
        return self.list(request, *args, **kwargs)

2.CreateModelMixin

创建视图扩展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。

如果序列化器对前端发送的数据验证失败,返回400错误。

源代码:

Copyclass CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        # 获取序列化器
        serializer = self.get_serializer(data=request.data)
        # 验证
        serializer.is_valid(raise_exception=True)
        # 保存
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {
    
    'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {
    
    }

3. RetrieveModelMixin

详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。

如果存在,返回200, 否则返回404。

源代码:

Copyclass RetrieveModelMixin(object):
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        # 获取对象,会检查对象的权限
        instance = self.get_object()
        # 序列化
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

举例:

Copyclass BookDetailView(RetrieveModelMixin, GenericAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    def get(self, request, pk):
        return self.retrieve(request)

4.UpdateModelMixin

更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。

同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。

成功返回200,序列化器校验数据失败时,返回400错误。

源代码:

Copyclass UpdateModelMixin(object):
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {
    
    }

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

5.DestroyModelMixin

删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。

成功返回204,不存在返回404。

源代码:

Copyclass DestroyModelMixin(object):
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

但是这样还是有局限性,我们还是要写两个视图类,配置两个路由

class Book(ListModelMixin, CreateModelMixin, GenericAPIView):
    queryset = models.Book.objects
    serializer_class = serializer.BookModel
    
    def get(self, request, *args, **kwargs):
        # 调用继承的ListModelMixin的list方法并返回
        return self.list(request, *args, **kwargs)
    
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class BookInfo(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
    queryset = models.Book.objects
    serializer_class = serializer.BookModel
    
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)
    
    def patch(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)
    
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

有没有更好地方法呢?

有的!

四、子类视图(9个)

1.CreateAPIView

提供 post 方法

继承自: GenericAPIView、CreateModelMixin

2.ListAPIView

提供 get 方法

继承自:GenericAPIView、ListModelMixin

3.RetrieveAPIView

提供 get 方法

继承自: GenericAPIView、RetrieveModelMixin

4.DestoryAPIView

提供 delete 方法

继承自:GenericAPIView、DestoryModelMixin

5.UpdateAPIView

提供 put 和 patch 方法

继承自:GenericAPIView、UpdateModelMixin

6.RetrieveUpdateAPIView

提供 get、put、patch方法

继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin

7.RetrieveUpdateDestoryAPIView

提供 get、put、patch、delete方法

继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

8.ListCreateAPIView:继承

有get获取所有,post方法新增

继承自:ListModelMixin,CreateModelMixin,GenericAPIVie

9.RetrieveDestroyAPIView

有get方法获取一条,delete方法删除

继承自:RetrieveModelMixin,DestroyModelMixin,GenericAPIView

使用方法

class Book(ListCreateAPIView, RetrieveDestroyAPIView, ):
    # 根据多继承python3的广度优先,他会从左到右依次匹配相关的方法,进而执行
    queryset = models.Book.objects
    serializer_class = serializer.BookModel

有一点需要注意的是,单记录查询和多记录查询是不冲突的,因为他们用的接口是不一样的!

那么有没有连借口都不用写的方法呢?

五、视图集

猜你喜欢

转载自blog.csdn.net/A1L__/article/details/109541009