DRF视图和路由

Posted dong-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DRF视图和路由相关的知识,希望对你有一定的参考价值。

APIView :

在django中写CBV的时候是继承View, rest_framework继承的是APIView, 这两种具体有什么不同呢?

urlpatterns = [
    url(r^book$, BookView.as_view()),
    url(r^book/(?P<id>d+)$, BookEditView.as_view()),
]

无论是View还是APIView最开始调用的都是as_view()方法, 看源码:

技术分享图片

  可以看到, APIView继承了View, 并且执行了View中的as_view()方法, 最后把view返回, 用csrf_exempt()方法包裹后去掉了csrf的认证.

 

而在View中的as_view()方法如下:

技术分享图片

  在View中的as_view方法返回了view函数, 而view函数执行了self.dispatch()方法, 但是这里的dispatch方法应该是APIView中的.

技术分享图片

 

 再去initialize_request中看下把什么赋值给了request, 并且赋值给了self.request, 也就是在视图中用的request.xxx到底是什么?

技术分享图片

  可以看到, 这个方法返回的是Request这个类的实例对象, 而这个Request类中的第一个参数request, 使我们在django中使用的request.

 

技术分享图片

  可以看到, 这个Request类把原来的request赋值给了self._request, 也就是说_request就是我们原先的request, 新的request使我们这个Request类.

 

那继承APIView之后请求来的数据都在哪呢?

技术分享图片

当我们使用了rest_framework框架之后, 我们的request是重新封装的Request类.

request.query_params 存放的是我们get请求的参数.

request.data 存放的是我们所有的数据, 包括post请求的以及put, patch请求.

相比原来的django的request, 我们现在的request更加精简, 清晰.

 


 

封装代码 :

原代码(封装之前) :

技术分享图片
from django.conf.urls import url
from SerDemo import views

urlpatterns = [
    # 第一二版本
    # url(r‘^book/$‘, views.BookView.as_view()),
    # url(r‘^book/(?P<edit_id>d+)‘, views.BookEditView.as_view()),

]
url
技术分享图片
from rest_framework import serializers
from app01 import models


class PublisherSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)


class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)


# 一个判断某一个字段是否含有敏感信息的函数, 直接调用即可
def my_validate(value):
    if "敏感信息" in value.lower():
        raise serializers.ValidationError("存在敏感词汇!!!")


class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)  # required 为False时, 反序列化不做校验
    title = serializers.CharField(max_length=32, validators=[my_validate])
    pub_time = serializers.DateField()
    category = serializers.CharField(source="get_category_display", read_only=True)
    # 自定义一个字段只用来反序列化接收使用
    post_category = serializers.IntegerField(write_only=True)

    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)
    # 多对多有many参数
    author = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)

    # 新增数据要重写的create方法
    def create(self, validated_data):
        # validated_data是验证通过的数据
        # 通过ORM操作给Book表增加数据
        # 添加除多对多字段的所有字段
        book_obj = models.Book.objects.create(
            title=validated_data["title"],
            pub_time=validated_data["pub_time"],
            category=validated_data["post_category"],
            publisher_id=validated_data["publisher_id"],
        )
        # 添加多对多字段
        book_obj.author.add(*validated_data["author_list"])
        return book_obj

    # 更新数据要重写update方法
    def update(self, instance, validated_data):
        # instance 是要更新的对象
        # 对除多对多字段以外的字段进行更新, 并设置当前已存在的数据为默认值
        instance.title = validated_data.get("title", instance.title)
        instance.pub_time = validated_data.get("pub_time", instance.pub_time)
        instance.category = validated_data.get("post_category", instance.category)
        instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
        # 判断前端传过来的数据是否含有author_list字段, 如果有则更新, 没有就不变动
        if validated_data.get("author_list"):
            instance.author.set(validated_data["author_list"])
        instance.save()
        return instance


# 对前端传过来的数据进行条件控制
def validate(self, attrs):
    # 相当于钩子函数
    # attrs是一个字典, 含有传过来的所有字段
    if "python" in attrs["title"].lower() and attrs["post_category"] == 1:
        return attrs
    else:
        raise serializers.ValidationError("分类或标题不匹配")
序列化器
技术分享图片
from rest_framework.views import APIView
from app01 import models
from .serializers import BookSerializer
from rest_framework.response import Response

from rest_framework.viewsets import ViewSetMixin



# Create your views here.


# 版本一
class BookView(APIView):
    def get(self, request):
        book_queryset = models.Book.objects.all()
        # 用序列化器进行序列化
        ser_obj = BookSerializer(book_queryset, many=True)

        return Response(ser_obj.data)

    def post(self, request):
        # 接收前端传过来的数据
        book_obj = request.data
        # 对前端传过来的数据使用自定义序列化方法进行校验(是否合法等)
        ser_obj = BookSerializer(data=book_obj)
        # 如果校验通过做些什么
        if ser_obj.is_valid():
            ser_obj.save()
            # validated_data是校验通过之后的数据
            return Response(ser_obj.validated_data)
        # 验证不通过返回错误信息
        return Response(ser_obj.errors)


class BookEditView(APIView):
    def get(self, request, edit_id):
        book_obj = models.Book.objects.filter(id=edit_id).first()
        ser_obj = BookSerializer(book_obj)
        return Response(ser_obj.data)

    def put(self, request, edit_id):
        book_obj = models.Book.objects.filter(id=edit_id).first()
        ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.validated_data)
        return Response(ser_obj.errors)

    def delete(self, request, edit_id):
        book_obj = models.Book.objects.filter(id=edit_id).first()
        if not book_obj:
            return Response("删除对象不存在!")
        book_obj.delete()
        return Response("删除成功了呢!")
view.py

第一次封装 :

技术分享图片
from django.conf.urls import url
from SerDemo import views

urlpatterns = [
    # 第一二版本
    # url(r‘^book/$‘, views.BookView.as_view()),
    # url(r‘^book/(?P<edit_id>d+)‘, views.BookEditView.as_view()),


]
url
技术分享图片
class GenericAPIView(APIView):
    """
    定义一个公共的类, 用来获取需要的资源
    """
    # 设置默认操作的数据为空
    queryset = None
    # 设置需要使用的序列化器为空
    serializer_class = None

    def get_queryset(self):
        """
        定义一个获取需要操作的数据的函数
        子类函数直接调用即可
        子类中调用此方法, 返回值中的self指的是调用此方法的子类的实力化对象
        queryset属性在本类中是None, 每一个继承此类的子类中都会重写queryset和serializer_class方法
        然后子类中执行此类中的方法是去执行子类对应的属性.
        :return:
        """
        return self.queryset.all()

    def get_serializer_class(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)


class ListModelMixin:
    """
    定义一个展示类,将展示方法统一写成一个方法
    """

    def list(self, request):
        queryset = self.get_queryset()
        ser_obj = self.get_serializer_class(queryset, many=True)
        return Response(ser_obj.data)


class CreateModeMixin:
    """
    新增的视图类
    """

    def create(self, request):
        ser_obj = self.get_serializer_class(data=request.data)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.validated_data)
        return Response(ser_obj.errors)


class EditModeMixin:
    """
    编辑的视图类
    """

    def retrieve(self, request, id):
        obj = self.get_queryset().filter(id=id).first()
        ser_obj = self.get_serializer_class(obj)
        return Response(ser_obj.data)


class UpdateModeMixin:
    """
    更新的视图类
    """

    def update(self, request, id):
        obj = self.get_queryset().filter(id=id).first()
        ser_obj = self.get_serializer_class(instance=obj, data=request.data, partial=True)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.validated_data)
        return Response(ser_obj.errors)


class DeleteModeMixin:
    """
    删除的视图类
    """

    def destroy(self, request, id):
        obj = self.get_queryset().filter(id=id).first()
        if not obj:
            return Response("删除的对象不存在!")
        obj.delete()
        return Response("删除了呢")


class ListCreateAPIview(GenericAPIView, ListModelMixin, CreateModeMixin):
    pass


class OperationAPIview(GenericAPIView, EditModeMixin, UpdateModeMixin, DeleteModeMixin):
    pass


class BookView(ListCreateAPIview):
    # 定义queryset属性时, DRF方法内部和关键字重名, 内部会识别并将此属性做缓存
    # 换个名字DRF不识别属性, 不做缓存, 也就不需要.all()
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)


class BookEditView(OperationAPIview):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer

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

    def put(self, request, edit_id):
        return self.update(request, edit_id)

    def delete(self, request, edit_id):
        return self.destroy(request, edit_id)
view.py

  我们封装的GenericAPIView, , 包括封装的每个方法的类, 其实框架都帮我们封装好了,

  我们可以继承这个二类, 来实现上面的视图.

其中框架还给我们提供了一个路由传参的方法:

技术分享图片

  actioon这个默认参数其实就是接收路由参数的参数.

再次封装 :

技术分享图片
from SerDemo import views

urlpatterns = [

    # 第三版
    # url(r‘^book/$‘, views.BookModelView.as_view({"get": "list", "post": "create"})),
    # url(r‘^book/(?P<pk>d+)‘, views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]
url
技术分享图片
from rest_framework.viewsets import ViewSetMixin


# 重写了源码中的as_view()方法, 是as_view方法可以传参数
# 在执行dispatch()方法之前
class ModelViewSet(ViewSetMixin, ListCreateAPIview, OperationAPIview):
    pass


class BookModelView(viewsets.ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer
view.py

  这样我们的视图只要写两行就可以了

  其实我们所写的所有视图, 框架都帮我们封装好了.

  注意 :

    应框架封装的视图, url上的那个关键字参数要用pk, 系统默认的

 


 

继承顺序图 :

技术分享图片

 


 

DRF路由 :

# 最终版
# 帮助我们生成带参数的路由
from rest_framework.routers import DefaultRouter
# 实例化DefaultRouter对象
router = DefaultRouter()
# 注册我们的路由以及视图
router.register(r"book", views.BookModelView)


urlpatterns = [

]

urlpatterns += router.urls

可以看到, 通过框架可以把路由视图都变得非常简单, 但是需要自定制的时候还是需要自己用APIView写, 当不需要那么多路由时候, 也不需药使用这种路由注册.

以上是关于DRF视图和路由的主要内容,如果未能解决你的问题,请参考以下文章

DRF视图和路由

drf--视图,路由

DRF之路由系统与视图

drf4 视图与路由组件

77-drf视图家族及路由层补充

drf之视图类与路由