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写, 当不需要那么多路由时候, 也不需药使用这种路由注册.

原文地址:https://www.cnblogs.com/dong-/p/9974646.html

时间: 2024-10-08 02:12:29

DRF视图和路由的相关文章

使用DRF视图集时自定义action方法

在我们用DRF视图集完成了查找全部部门,创建一个新的部门,查找一个部门,修改一个部门,删除一个部门的功能后,views.py的代码是这样子的: class DepartmentViewSet(ModelViewSet): queryset = Department.objects.all() serializer_class = DepartmentSerializer 现在需要新增一个功能,查询最新成立的部门. 这时,我们就需要自定义action方法了. 新增功能后的代码如下: class D

def --- DRF视图的详细用法

视图的用法 1.DRF中的request以及response DRF中传入视图的request对象 不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象. DRF中传入视图的response对象 REST framework提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型. 添加配置文件 REST_FRAMEWORK = { 'D

AngularJs多重视图和路由的使用

使用AngularJs来做多重视图和路由是在方便不过了,在开发过程中,都有许多的页面,而这些页面都有相同的部分,比如页面的头部和尾部通常都是一样的,变化的都是主体部分,还有就是一些后端管理的一些项目,通常不变的都是头部,尾部和菜单部分,变化的都是右边的内容部分,使用AngularJs的多重视图和路由就可以很方便的实现这样的效果.在实现之前需要准备两个文件,一个是angular的主JS文件,另一个是angular的路由JS文件,如下: <script src="js/angular.min.

[Angularjs]视图和路由(二)

写在前面 上篇文章主要介绍了视图和路由的基本概念,并在文章最后举了一个简单的使用案例.这篇文章将继续学习路由的配置,及相关参数的说明. 系列文章 [Angularjs]ng-select和ng-options [Angularjs]ng-show和ng-hide [Angularjs]视图和路由(一) 参数 when方法,上篇文章也提到了配置路由用到的when方法,对when方法的第二个参数可以接收哪些属性,这里将一一列举.1.controller controller:'MyControlle

[Angularjs]视图和路由(四)

写在前面 关于angularjs的路由的概念基本上这篇就要结束了,通过学习,以及在实际项目中的实践,还是比较容易上手的.自己也通过angularjs做了一个在app上的一个模块,效果还是可以的. 系列文章 [Angularjs]ng-select和ng-options [Angularjs]ng-show和ng-hide [Angularjs]视图和路由(一) [Angularjs]视图和路由(二) [Angularjs]视图和路由(三) 路由模式 不同的路由模式在浏览器的地址栏中会以不同的UR

[Angularjs]视图和路由(三)

写在前面 上篇文章主要介绍了路由中when方法的第二个参数,常见的几个属性,以及作用.本篇文章,将介绍和路由相关的几个常见的服务. 系列文章 [Angularjs]ng-select和ng-options [Angularjs]ng-show和ng-hide [Angularjs]视图和路由(一) [Angularjs]视图和路由(二) $routeParams 有这样一种情况,在设计路由的时候,我们希望在路由上面传递参数,但是当跳转到指定的路由的时候,我们该如何接收呢?这就用到$routePa

关于视图和路由的进阶技巧

关于视图和路由的进阶技巧 视图装饰器 Python装饰器让我们可以用其他函数包装特定函数. 当一个函数被一个装饰器"装饰"时,那个装饰器会被调用,接着会做额外的工作,修改变量,调用原来的那个函数.我们可以把我们想要重用的代码作为装饰器来包装一系列视图. 装饰器的语法看上去像这样: @decorator_function def decorated(): pass 如果你看过Flask入门指南,那么对这个语法应该不敢到陌生.@app.route正是用于在Flask应用中给视图函数设定路由

Django框架的使用教程--视图和路由[二]

视图和路由 1.创建一个django_test应用 2.setting中设置django_test INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django_test.apps.D

【DRF视图】

目录 开始使用内置视图 请结合[DRF序列化]此文献中的数据文件及序列化文件来阅读如下代码. DRF视图为我们提供了非常简便的方法--内置了增删改查等一系列的操作. 我们只需在url中指定对应的方法,视图继承内置方法的类,即可实现两三行代码搞定一个请求. @ * 我们先看看仿照内置方法实现的** APIView视图文件: from rest_framework.views import APIView from rest_framework.response import Response fr