drf序列化器之请求、响应以及视图

目录:

 

1. http请求处理

drf除了在数据序列化部分简写代码以外,还在视图中提供了简写操作。所以在django原有的django.views.View类基础上,drf封装了多个视图子类出来提供给我们使用。

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

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

  • 控制数据库查询的执行

  • 调用请求类和响应类[这两个类也是由drf帮我们再次扩展了一些功能类。

为了方便我们学习,所以先创建一个子应用req

python manage.py startapp req

注册子应用:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # 注册 rest_framework以及子应用req
    'rest_framework',
    'req',     # 请求与响应
]

注册路由

# 子应用req路由
from django.urls import path
from . import views
urlpatterns = [
    path("students1/",views.StutentView.as_view()),
]


# 总路由defdemo/urls.py
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('req/', include("req.urls")),
]

1.1. 请求与响应

我们先看一下django的request和response

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from django.views import View

# Create your views here.
# django视图中的请求和响应
class StutentView(View):
    def get(self,request):
        print(request)  # <WSGIRequest: GET '/req/students1/'>,WSGIRequest的实例对象,WSGIRequest是HttpRequest的子类
        response = HttpResponse("ok")
        print(response)    # <HttpResponse status_code=200, "text/html; charset=utf-8">
        return response

postman测试

浏览器:

drf的视图

路由:

# req子路由
from django.urls import path
from . import views
urlpatterns = [
    path("students1/",views.StutentView.as_view()),
    path("students2/",views.StudentAPIView.as_view()),
]

# 总路由drfdemo/urls.py
from django.contrib import admin
from django.urls import path,include

# 2.x之后django把url拆分成了两个路由函数
# django.urls.path 专门编写字符串路由
# django.urls.re_path 专门编写正则路由
urlpatterns = [
    path('admin/', admin.site.urls),
    path('req/', include("req.urls")),
]

视图

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from django.views import View

# Create your views here.
# django视图中的请求和响应
class StutentView(View):
    def get(self,request):
        print(request)  # <WSGIRequest: GET '/req/students1/'>
        response = HttpResponse("ok")
        print(response)    # <HttpResponse status_code=200, "text/html; charset=utf-8">
        return response


from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework import status
class StudentAPIView(APIView):
    # 继承的视图类决定了方法中的request对象到底是那个请求对象。
    def get(self,request):
        print(request)    # <rest_framework.request.Request object at 0x7f17d5b07080>
        # rest_framework.request.Request是drf自己独立声明的,不是django的子类
        # drf的request对象中有一个属性叫 _request,这个属性对应的就是django的HttpRequest对象

        response = Response("ok")
        # rest_framework.response.Response
        # drf的response对象就是django的HttpResponse的子类
        print(response)
        return response

postman:

 浏览器:

小结:

"""
在django中,我们可以在视图里面使用request和response接收和返回数据给客户端,
主要是因为当前视图类继承了django.views.View的原因

在drf中,我们可以在视图里面使用request和response接收和返回数据给客户端,
这里主要是因为视图类集成了rest_framework.views.APIView,所有有好看的界面。
"""

1.1.1 Request

REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象。

REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析 HTML 表单内容。request.data 是一个 QueryDict 字典,包含所有表单参数;解析JSON,request.data是一个字典

DRF默认提供解析器如下:

JSONParser,解析json格式数据({"user": "zhubao"}),解析成功后,request.data类型为dict

FormParser,解析form表单格式数据(user=zhubao),解析成功后,request.data类型为QuerySet,如果QuerySet对象想转换成传统字典,request.data.dict()

MultiPartParser(用的较少),解析较为复杂的form表单格式数据,解析成功后,request.data类型为QuerySet

FileUploadParser,解析文件上传格式的数据,解析成功后,request.data的值为一个带有key为'file'的dict,也可以用request.FILES.get('key')

补充:

 Request对象:request._request,request.data,重写了__getattr__,request.method-->去原生request中拿

# 前端传过来的数据从那取?
        -地址栏里:request.GET/query_params
        -请求体中的数据:request.data/POST(如果配置去掉了JSONParser解析器,则json格式解释不了需要手动到request.body中取)
        -请求头中数据:request.META.get("大写")  # eg:request.META.get('REMOTE_ADDR')

Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。

无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。

from rest_framework.request import Request

1.1.1.1 常用属性

1).data

request.data 返回解析之后的请求体数据。类似于Django中标准的request.POSTrequest.FILES属性,但提供如下特性:

  • 包含了解析之后的文件和非文件数据

  • 包含了对POST、PUT、PATCH请求方式解析后的数据

  • 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据

2).query_params

request.query_params返回解析之后的查询字符串数据(http://127.0.0.1:8000/req/students2/?name="oldboy"),就是查询地址?后面一堆k,v

request.query_params与Django标准的request.GET相同,只是更换了更正确的名称而已。

示例:

路由:

# req子路由
from django.urls import path
from . import views
urlpatterns = [
    path("students1/",views.StutentView.as_view()),
    path("students2/",views.StudentAPIView.as_view()),
]

# 总路由drfdemo/urls.py
from django.contrib import admin
from django.urls import path,include

# 2.x之后django把url拆分成了两个路由函数
# django.urls.path 专门编写字符串路由
# django.urls.re_path 专门编写正则路由
urlpatterns = [
    path('admin/', admin.site.urls),
    path('req/', include("req.urls")),
]

 视图:

from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from django.views import View


# Create your views here.
# django视图中的请求和响应
class StutentView(View):
    def get(self, request):
        print(request)  # <WSGIRequest: GET '/req/students1/'>,WSGIRequest的实例对象,WSGIRequest是HttpRequest的子类
        response = HttpResponse("ok")
        print(response)  # <HttpResponse status_code=200, "text/html; charset=utf-8">
        return response


from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework import status
class StudentAPIView(APIView):
    # 继承的视图类决定了方法中的request对象到底是那个请求对象。
    def get(self,request):
        response = Response("ok")
        return response

    def post(self, request):
        print(request.data.dict())  # 接收http请求体数据
        """
        打印效果
        如果客户端上传的是json:
        {'title': '老人与海', 'pub_date': '2022-3-30', 'read': 1, 'comment': 0, 'price': -17}
        
        如果客户端上传的是form表单:
        <QueryDict: {'title': ['python']}>
        如果QuerySet对象想转换成传统字典使用request.data.dict()就会变成:{'title': 'python'}
        
        """
        response = Response("ok")
        return response

postman测试:

传json

 传表单

1.1.2 Response

from rest_framework.response import Response

REST framework提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染器)成符合前端需求的类型。

REST framework提供了Renderer 渲染器,用来根据请求头中的Accept(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。【简而言之,就是Renderer能通过请求找的Accept查询出客户端支持和希望的数据类型,把视图的结果以客户端能识别的格式返回】

可以在rest_framework.settings.py查找所有的drf默认配置项

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览器API渲染器
    )
}

1.1.2.1 构造方式

Response(data, status=None, template_name=None, headers=None, exception=False, content_type=None)

data数据不要是render处理之后的数据,只需传递python的内建类型数据即可,REST framework会使用renderer渲染器处理data

data不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用Serializer序列化器序列化处理后(转为了Python字典类型)再传递给data参数。

参数说明:

  • data: 为响应准备的序列化处理后的数据;serializer.data、或者{"message":"ok"}

  • status: 状态码,默认200;

  • template_name: 模板名称,如果使用HTMLRenderer 时需指明;

  • headers: 用于存放响应头信息的字典;例如可以放token

  • content_type: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数。

1.1.2.2 常用属性

1).data

传给response对象的序列化后,但尚未render处理的数据

2).status_code

状态码的数字

3).content

经过render处理后的响应数据

1.1.2.3 状态码

为了方便设置状态码,REST framewrok在rest_framework.status模块中提供了常用状态码常量。

from rest_framework import status
1)信息告知 - 1xx
HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS
2)成功 - 2xx
HTTP_200_OK   # 常规请求
HTTP_201_CREATED  # 创建成功
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS
3)重定向 - 3xx
HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY   # 重定向是永久的重定向,搜索引擎在抓取新内容的同时也将旧的网址替换为重定向之后的网址
HTTP_302_FOUND   # 跳转是暂时的跳转,搜索引擎会抓取新的内容而保留旧的网址。因为服务器返回302代码,搜索引擎认为新的网址只是暂时的
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT
4)客户端错误 - 4xx
HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN     # 服务器拒绝访问,很可能是没有权限
HTTP_404_NOT_FOUND     # 无法找到文件
HTTP_405_METHOD_NOT_ALLOWED    # 请求方法不存在
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
5)服务器错误 - 5xx
HTTP_500_INTERNAL_SERVER_ERROR   # 服务端异常
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED

Request和Reponse视图中使用案例

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework import status
class StudentAPIView(APIView):
    # 继承的视图类决定了方法中的request对象到底是那个请求对象。这里是APIView所以request是drf中的
    def get(self,request):
        print(request)
        # rest_framework.request.Request是drf自己独立声明的,不适django的子类
        # drf的request对象中有一个属性叫 _request,这个属性对应的就是django的HttpRequest对象

        response = Response("ok")
        # rest_framework.response.Response
        # drf的response对象就是django的HttpResponse的子类
        print(response)
        return response

    def post(self,request):
        """请求对象的学习"""
        print(request.data)  # 接受http请求体数据,目前默认的配置中只能接受表单和json数据,
        # 其他格式的数据不能request.data来获取
        """打印效果:
        如果客户端上传的是json:
        {'title': '西游记', 'price': 20, 'pub_date': '2020-10-10', 'read': 200, 'comment': 20}
        如果客户端上传的是表单
        <QueryDict: {'title': ['老男孩']}>
        """
        print(request.query_params)  # 这里其实本质上就是django.http.request.HttpRequest对象的GET属性
        
        """
        请求: /req/students2/?name=xiaoming
        打印效果:
        <QueryDict: {'name': ['xiaoming']}>
        """
        return Response(data="ok", status=status.HTTP_201_CREATED, headers={"company": "laonanhai"})

自定义封装Response

1、路由

# req子路由
from django.urls import path
from . import views
urlpatterns = [
    path("students1/",views.StutentView.as_view()),  # 测试自定义封装Response
]

# 总路由drfdemo/urls.py
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('req/', include("req.urls")),
]

2、在子应用下新建utils.py

class MyResponse():
    def __init__(self):
        self.status=100
        self.msg='成功'
    @property
    def get_dict(self):
        return self.__dict__

if __name__ == '__main__':
    res=MyResponse()
    res.status=101
    res.msg='查询失败'
    # res.data={'name':'lqz'}
    print(res.get_dict)

3、在views.py中使用

from django.http import JsonResponse
from django.views import View
from .utils import MyResponse


# Create your views here.
# django视图中的请求和响应
class StutentView(View):
    def get(self, request):
        res = MyResponse()
        res.status = 101
        res.msg = '查询失败'
        res.data = {'name': 'lqz'}
        print(res.get_dict)
        return JsonResponse(res.get_dict)

postman测试

通过继承Response,重新封装MyResponse

myresponse.py

from rest_framework.response import Response


class MyResponse(Response):
    def __init__(self, status=0, msg='ok', headers=None, exception=False, content_type=None,
                 **kwargs):
        data = {
            'status': status,
            'msg': msg
        }
        # 在外界数据可以用result和results来存储
        if kwargs:
            data.update(kwargs)
        super().__init__(data=data, headers=headers, exception=exception, content_type=content_type)

views.py

from rest_framework.views import APIView
from .models import Book
from app01.serializers import BookSerializer
from app01.myresponse import MyResponse

# 基于APIView写的
class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        serializer = BookSerializer(instance=book_list, many=True)
        return MyResponse(200, "获取成功", result=serializer.data)

urls.py

from django.contrib import admin
from django.urls import path,re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view()),
]

执行效果

全局和局部配置响应的样子

浏览器响应成浏览器的格式,postman响应成json格式,通过配置实现的(默认配置)

#不管是postman还是浏览器,都返回json格式数据
# drf有默认的配置文件---》先从项目的setting中找,找不到,采用默认的
# drf的配置信息,先从自己类中找--》项目的setting中找---》默认的找
    -局部使用:对某个视图类有效
        -在视图类中写如下
        from rest_framework.renderers import JSONRenderer
        renderer_classes=[JSONRenderer,]
    -全局使用:全局的视图类,所有请求,都有效
        -在setting.py中加入如下,REST_FRAMEWORK变量中都是drf的配置信息
        REST_FRAMEWORK = {
            'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
                'rest_framework.renderers.JSONRenderer',  # json渲染器
                'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器
            )
        }

局部使用示例:

views.py

from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.renderers import JSONRenderer   # 要启用的配置先导入


# Create your views here.
class TestView(APIView):
    renderer_classes = [JSONRenderer, ]   # 重写APIView中renderer_classes,只对TestView视图类有效
    def get(self, request):
        print(request)

        return Response({"name": "alias"}, status=201, headers={"token": "xxx"})

2. 视图

2.1 drf提供的视图主要作用

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

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

  • 控制数据库查询的执行[数据库的删除/查询代码写在视图中,更新和添加写在序列化器]

2.2 视图

REST framework 提供了众多的通用视图基类与扩展类,以简化视图的编写。

drf视图的四大核心:APIView、GenericAPIView、视图集和视图扩展类

2.2.1 2个视图基类

2.2.1.1 APIView

from rest_framework.views import APIView

APIView是REST framework提供的所有视图的基类,继承自Django的View父类。

drf的APIView与django的View的不同之处在于:

  • 传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;

  • 视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;

  • 任何APIException异常都会被捕获到,并且处理成合适的响应信息;

  • 重写了as_view(),在进行dispatch()路由分发前,会对http请求进行身份认证、权限检查、访问流量控制。

支持定义的类属性

  • authentication_classes 列表或元组,身份认证类

  • permissoin_classes 列表或元组,权限检查类

  • throttle_classes 列表或元祖,流量控制类

举例:在APIView中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。

子应用demo/views.py

# Create your views here.
"""APIView是drf里面提供的所有视图类的父类
   APIView提供的功能/属性/方法是最少的,所以使用APIView基本类似我们使用django的View
"""
"""
GET   /students/ 获取多个学生信息 
POST  /students/ 添加一个学生信息

GET    /students/<pk>/  获取一个学生信息 
PUT    /students/<pk>/  修改一个学生信息
DELETE /students/<pk>/  删除一个学生信息
"""
from rest_framework.views import APIView
from students.models import Student
from .serializers import StudentModelSerializer
from rest_framework.response import Response
from rest_framework import status

class StudentAPIView(APIView):
    def get(self,request):
        # 1. 获取学生信息的数据模型
        student_list = Student.objects.all()
        # 2. 调用序列化器
        serializer = StudentModelSerializer(instance=student_list, many=True)
        # 3. 返回数据
        return Response(serializer.data)

    def post(self,request):
        # 1. 调用序列化器对用户提交的数据进行验证
        serializer = StudentModelSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 2. 调用序列化器进行数据库操作
        instance = serializer.save() # save()方法返回的是添加成功以后的模型对象

        serializer = StudentModelSerializer(instance=instance)

        # 3. 返回新增数据
        return Response(serializer.data, status=status.HTTP_201_CREATED)


class Student2APIView(APIView):
    def get(self,request,pk):
        # 1. 根据pk获取模型对象
        student = Student.objects.get(pk=pk)
        # 2. 序列化器转换数据
        serializer = StudentModelSerializer(instance=student)
        # 3. 响应数据
        return Response(serializer.data)

    def put(self,request,pk):
        # 1. 通过pk查询学生信息
        student = Student.objects.get(pk=pk)

        # 3. 调用序列化器对客户端发送过来的数据进行验证
        serializer = StudentModelSerializer(instance=student, data=request.data)
        serializer.is_valid(raise_exception=True)
        # 4. 保存数据
        instance = serializer.save()

        serializer = StudentModelSerializer(instance=instance)

        # 5. 返回结果
        return Response(serializer.data, status=status.HTTP_201_CREATED)

    def delete(self, request, pk):
        # 1. 通过pk查询学生信息
        Student.objects.get(pk=pk).delete()
        return Response({"message":"ok"}, status=status.HTTP_204_NO_CONTENT)

子应用demo/serializers.py

from rest_framework import serializers
from students.models import Student
class StudentModelSerializer(serializers.ModelSerializer):
    """学生序列化器"""
    class Meta:
        model = Student
        fields = "__all__"

总路由defdemo/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('demo/', include("demo.urls")),
]

demo/urls.py

from django.urls import path,re_path
from . import views
urlpatterns = [
    path("students1/",views.Student1APIView.as_view()),
    re_path("students2/(?P<pk>\d+)/", views.Student2APIView.as_view()),
]

APIView源码分析

#from rest_framework.views import APIView
# urls.py
path('booksapiview/', views.BooksAPIView.as_view()),  #在这个地方应该写个函数内存地址

#APIView的as_view方法(类的绑定方法)
   def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs)  # 调用父类(View)的as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs
        # 以后所有的请求,都没有csrf认证了,只要继承了APIView,就没有csrf的认证
        return csrf_exempt(view)
 

#请求来了---》路由匹配上---》view(request)---》调用了self.dispatch(),会执行apiview的dispatch
    
# APIView的dispatch方法
    def dispatch(self, request, *args, **kwargs):

        self.args = args
        self.kwargs = kwargs
        # 重新包装成一个request对象,以后再用的request对象,就是新的request对象了
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            # 三大认证模块
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            # 响应模块
            response = handler(request, *args, **kwargs)

        except Exception as exc:
            # 异常模块
            response = self.handle_exception(exc)

        # 渲染模块
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
   
# APIView的initial方法
     def initial(self, request, *args, **kwargs):
        # 认证组件:校验用户 - 游客、合法用户、非法用户
        # 游客:代表校验通过,直接进入下一步校验(权限校验)
        # 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
        # 非法用户:代表校验失败,抛出异常,返回403权限异常结果
        self.perform_authentication(request)
        # 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色
        # 认证通过:可以进入下一步校验(频率认证)
        # 认证失败:抛出异常,返回403权限异常结果
        self.check_permissions(request)
        # 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
        # 没有达到限次:正常访问接口
        # 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
        self.check_throttles(request)

request

from rest_framework.request import Request
# 只要继承了APIView,视图类中的request对象,都是新的,也就是上面那个request的对象了
# 老的request在新的request._request
# 以后使用reqeust对象,就像使用之前的request是一模一样(因为重写了__getattr__方法)
  def __getattr__(self, attr):
        try:
            return getattr(self._request, attr) #通过反射,取原生的request对象,取出属性或方法
        except AttributeError:
            return self.__getattribute__(attr)

 # request.data 感觉是个数据属性,其实是个方法,@property,修饰了
    它是一个字典,post请求不管使用什么编码,传过来的数据,都在request.data
 #get请求传过来数据,从哪取?
    request.GET
    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET
    
    #视图类中
     print(request.query_params)  #get请求,地址中的参数
     # 原来在
     print(request.GET)

2.2.1.2 GenericAPIView

通用视图类主要作用就是把视图中的独特的代码抽取出来,让视图方法中的代码更加通用,方便把通用代码进行简写。

from rest_framework.generics import GenericAPIView

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

提供的关于序列化器使用的属性与方法

  • 属性:

    • serializer_class 指明视图使用的序列化器

  • 方法:

    • get_serializer_class(self)  

      当出现一个视图类(视图需要直接或间接继承ViewSetMixin)中调用多个序列化器时,那么可以通过在get_serializer_class方法中条件判断,根据不通的条件返回不同的序列化器类名,就可以让视图方法执行不同的序列化器对象了。

      返回序列化器类,默认返回serializer_class,可以重写,可以参考https://www.cnblogs.com/baicai37/p/13307783.html 例如:

      def get_serializer_class(self):
         if self.request.user.is_staff:
             return FullAccountSerializer
         return BasicAccountSerializer
    • get_serializer(self, args, *kwargs)

      返回序列化器对象,主要用来提供给Mixin扩展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法。

      注意,该方法在提供序列化器对象的时候,会向序列化器对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。

      • request 当前视图的请求对象

      • view 当前请求的类视图对象

      • format 当前请求期望返回的数据格式

提供的关于数据库查询的属性与方法

  • 属性:

    • queryset 指明使用的数据查询集,传queryset对象

      • 例如:queryset = Book.objects.all()
  • 方法:

    • get_queryset(self)

      返回视图使用的查询集(queryset对象),主要用来提供给Mixin扩展类使用,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写,例如:

      def get_queryset(self):
         user = self.request.user
         return user.accounts.all()
    • get_object(self)

      返回详情视图所需的模型类数据对象,主要用来提供给Mixin扩展类使用。

      在试图中可以调用该方法获取详情信息的模型类对象。

      若详情访问的模型类对象不存在,会返回404。

      该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。

    •  

      举例:

# url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
class BookDetailView(GenericAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    def get(self, request, pk):
        book = self.get_object() # get_object()方法根据pk参数查找queryset中的数据对象
        serializer = self.get_serializer(book)
        return Response(serializer.data)

其他可以设置的属性

  • pagination_class 指明分页控制类

  • filter_backends 指明过滤控制后端

get_object()源码分析,为什么get_object()所在视图对应的url必须是有名分组,而且传到视图的关键字必须是pk

-源码解析
            queryset = self.filter_queryset(self.get_queryset()) #返回所有数据queryset对象
            # lookup_url_kwarg就是pk,路由中有名分组分出来的pk
            lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
            # {pk:4}  4 浏览器地址中要查询的id号http://127.0.0.1:8000/books6/4/
            filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
            # 根据pk=4去queryset中get单个对象
            obj = get_object_or_404(queryset, **filter_kwargs)
            self.check_object_permissions(self.request, obj)
            return obj

所以,如果想改url传过来的关键字名称,在get_object()所在视图中添加

lookup_url_kwarg = "要设置的关键字名称"

GenericAPIView使用示例

 urls.py

from django.contrib import admin
from django.urls import path,re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # 使用GenericAPIView
    path('books2/', views.BookView2.as_view()),
    re_path('books2/(?P<pk>\d+)/', views.Book2DetailView.as_view()),  # 必须使用有名分组而且关键字为pk
]

views.py

from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from .models import Book
from app01.serializers import BookSerializer


# ===========================================================================
# 基于GenericAPIView写
class BookView2(GenericAPIView):
    # queryset要传queryset对象,查询了所有的图书
    # serializer_class使用哪个序列化类来序列化这堆数据
    queryset = Book.objects
    # queryset = Book.objects.all()
    serializer_class = BookSerializer
    def get(self, request):
        book_list = self.get_queryset()
        serializer = self.get_serializer(book_list,many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response({'status': 101, 'msg': '添加失败'})


class Book2DetailView(GenericAPIView):
    queryset = Book.objects
    serializer_class = BookSerializer
    def get(self, request,pk):
        book = self.get_object()
        book_ser = self.get_serializer(book)
        return Response(book_ser.data)

    def put(self, request,pk):
        book = self.get_object()
        book_ser = self.get_serializer(instance=book,data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'status': 101, 'msg': '校验失败'})

    def delete(self,request,pk):
        ret=self.get_object().delete()
        return Response({'status': 100, 'msg': '删除成功'})

models.py

from django.db import models


# Create your models here.
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish = models.CharField(max_length=32)

serializers.py

from rest_framework import serializers
from app01.models import Book


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"

 2.2.2 基于GenericAPIView 的5个视图扩展类写的接口

urls.py

from django.contrib import admin
from django.urls import path,re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # 使用GenericAPIView
    path('books2/', views.BookView2.as_view()),
    re_path('books2/(?P<pk>\d+)/', views.Book2DetailView.as_view()),  # 必须使用有名分组而且关键字为pk
    # 使用GenericAPIView 5 个视图扩展类  重写的
    path('books3/', views.Book3View.as_view()),
    re_path('books3/(?P<pk>\d+)', views.Book3DetailView.as_view()),
]

views.py

from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from .models import Book
from app01.serializers import BookSerializer
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin


# Create your views here.
# ===========================================================================
# 基于GenericAPIView写
class BookView2(GenericAPIView):
    # queryset要传queryset对象,查询了所有的图书
    # serializer_class使用哪个序列化类来序列化这堆数据
    queryset = Book.objects
    # queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        book_list = self.get_queryset()
        serializer = self.get_serializer(book_list, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response({'status': 101, 'msg': '添加失败'})


class Book2DetailView(GenericAPIView):
    queryset = Book.objects
    serializer_class = BookSerializer

    def get(self, request, pk):
        book = self.get_object()
        book_ser = self.get_serializer(book)
        return Response(book_ser.data)

    def put(self, request, pk):
        book = self.get_object()
        book_ser = self.get_serializer(instance=book, data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'status': 101, 'msg': '校验失败'})

    def delete(self, request, pk):
        ret = self.get_object().delete()
        return Response({'status': 100, 'msg': '删除成功'})


# ===================================================================
# 基于GenericAPIView和5个视图扩展类写的接口
class Book3View(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Book.objects
    serializer_class = BookSerializer

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

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


class Book3DetailView(GenericAPIView, RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin):
    queryset = Book.objects
    serializer_class = BookSerializer

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

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

    def delete(self, request, pk):
        return self.destroy(request, pk)

2.2.3、GenericAPIView的视图子类

from rest_framework.generics import CreateAPIView

 urls.py

from django.contrib import admin
from django.urls import path,re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # 使用GenericAPIView 视图扩展类
    path('books4/', views.Book4View.as_view()),
    re_path('books4/(?P<pk>\d+)', views.Book4DetailView.as_view()),
]

views.py

from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from .models import Book
from app01.serializers import BookSerializer
from rest_framework.generics import CreateAPIView,ListAPIView,UpdateAPIView,RetrieveAPIView,DestroyAPIView


# Create your views here.
# ====================================
# 视图子类
class Book4View(ListAPIView,CreateAPIView):   # 获取所有和新增一个
    queryset = Book.objects
    serializer_class = BookSerializer


class Book4DetailView(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
    queryset = Book.objects
    serializer_class = BookSerializer

上传文件示例:

https://www.jianshu.com/p/5e2ce6b5ba5c

补充:pycharm中查看类的继承关系

1、

2、 

 2.2.4 ModelViewSet使用

views.py

from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from .models import Book
from app01.serializers import BookSerializer
from rest_framework.viewsets import ModelViewSet


# Create your views here.
# ===========================================================
# 使用ModelViewSet
class Book5View(ModelViewSet):  # 5个接口都有,但是路由有点问题
    queryset = Book.objects
    serializer_class = BookSerializer

urls.py

from django.contrib import admin
from django.urls import path,re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # 使用ModelViewSet
    path('books5/', views.Book5View.as_view(actions={'get':'list','post':'create'})), #当路径匹配,又是get请求,会执行Book5View的list方法
    re_path('books5/(?P<pk>\d+)', views.Book5View.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})),
]

2.2.4.1 源码分析ModelViewSet的父类ViewSetMixin

# 重写了as_view
# 核心代码(所以路由中只要配置了对应关系,比如{'get':'list'}),当get请求来,就会执行list方法
for method, action in actions.items():
    #method:get
    # action:list
    handler = getattr(self, action)
    #执行完上一句,handler就变成了list的内存地址
    setattr(self, method, handler)
    #执行完上一句  对象.get=list
    #for循环执行完毕 对象.get:对着list   对象.post:对着create

2.2.4.2 继承ViewSetMixin的视图类

1、

# views.py
from rest_framework.viewsets import ViewSetMixin
class Book6View(ViewSetMixin,APIView): #一定要放在APIVIew前
    def get_all_book(self,request):
        print("xxxx")
        book_list = Book.objects.all()
        book_ser = BookSerializer(book_list, many=True)
        return Response(book_ser.data)
    
# urls.py
    #继承ViewSetMixin的视图类,路由可以改写成这样
    path('books6/', views.Book6View.as_view(actions={'get': 'get_all_book'})),

 2、自动生成路由

# views.py
from rest_framework.viewsets import ViewSetMixin, ViewSet
from luffyapi.utils.response import APIResponse
from rest_framework.decorators import action  # 装饰器
from . import serializers
# 登录接口
class LoginView(ViewSet):
    @action(methods=['POST'], detail=False)
    def login(self, request, *args, **kwargs):
        # 1 需要 有个序列化的类
        login_ser = serializers.LoginModelSerializer(data=request.data, context={'request': request})
        # 2 生成序列化类对象
        # 3 调用序列号对象的is_validad
        # result = login_ser.is_valid()
        if not login_ser.is_valid():
            return APIResponse(status=101, msg='登录失败', errors=login_ser.errors)

        token = login_ser.context.get('token')
        user = login_ser.context.get('user')
        # 4 return
        return APIResponse(status=100, msg='登录成功', token=token, username=user.username)

# urls.py
from django.urls import path,re_path,include
from . import views
from rest_framework.routers import SimpleRouter


router = SimpleRouter()
router.register('', views.LoginView, 'login')

urlpatterns = [
    path('', include(router.urls)),
]

小练习

1 自定义User表,新增mobile唯一约束字段;新增icon图片字段
2 在自定义User表基础上,用 GenericViewSet + CreateModelMixin + serializer 完成User表新增接口(就是注册接口)(重要提示:序列化类要重写create方法,不然密码就是明文了)
3 在自定义User表基础上,用 GenericViewSet + RetrieveModelMixin + serializer 完成User表单查(就是用户中心)
4 在自定义User表基础上,用 GenericViewSet + UpdateModelMixin + serializer 完成用户头像的修改

代码:

https://www.cnblogs.com/baicai37/p/13307783.html

posted @ 2020-06-28 22:44  耗油炒白菜  阅读(657)  评论(0编辑  收藏  举报