APIView
1. APIView基本使用
drf是一个第三方的app,只能在django中使用
安装了drf后,导入一个视图类APIView,所有后期需要使用drf写的视图类,都是继承APIView及其子类
1.1 使用View+JsonResponse
# 创建表
models.py中
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
author = models.CharField(max_length=32)
publish = models.CharField(max_length=32)
# 路由
urls.py
from django.contrib import admin
from django.urls import path
from app01.views import BookView
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', BookView.as_view()),
]
# 写视图类
Views.py
from django.shortcuts import render, HttpResponse
from django.views import View
from app01 import models
from django.http import JsonResponse
class BookView(View):
def get(self, request):
book_list = models.Book.objects.all()
print(book_list) # <QuerySet [<Book: Book object (1)>]>
# 错误方式
"""
return JsonResponse(book_list) # book_list是queryset对象
不能直接序列化,只能通过for循环拼成列表套字典的方式
否则报错:
TypeError: In order to allow non-dict
objects to be serialized set the safe parameter to False.
"""
# 正确方式
res_list = []
for i in book_list:
res_list.append({'name': i.name, 'price': i.price, 'publish': i.publish, 'author': i.author})
return JsonResponse(res_list, safe=False, json_dumps_params={'ensure_ascii': False}) # 只能序列化字典和列表,
1.2 使用APIView的Response
views.py
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
class BookView(APIView):
def get(self, request):
book_list = models.Book.objects.all()
res_list = []
for i in book_list:
res_list.append({'name': i.name, 'price': i.price, 'publish': i.publish, 'author': i.author})
return Response(res_list)
settings.py
settings.py中注册
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework' # 注册这个drfapp
]
2. APIView源码分析
# urls.py
path('books/', BookView.as_view()),
# views.py
class BookView(APIView):
def get(self, request):
......
return Response(res_list)
视图类继承了APIview后,执行流程
path('books/', BookView.as_view()),
# 父类(APIView)中的as_view
1.先从父类APIView中查找as_view
def as_view(cls, **initkwargs):
# 父类APIView的父类(View)的as_view
2.父类APIView中的as_view又调用了APIView父类View的as_view方法
view = super().as_view(**initkwargs)
3. 父类View的as_view方法调用自身的闭包函数view返并返回view
return view
4. 通过csrf_exempt取消了csrf认证
# 从此以后继承APIView的视图类,所有的请求都没有csrf的校验了
return csrf_exempt(view)
5. 请求来了之后执行父类View的as_view方法中的闭包函数view
def view(request, *args, **kwargs):
return self.dispatch(request, *args, **kwargs)
6.view方法最后返回了一个dispatch方法
self.dispatch(request, *args, **kwargs)
# 父类APIView中的dispatch方法
7.从自身查找找到父类APIView中的dispatch方法
def dispatch(self, request, *args, **kwargs):
7.dispatch方法调用initialize_request方法将request对象重新封装成一个新的request对象
request = self.initialize_request(request, *args, **kwargs)
self.request = request
8.使用try
try:
8.1通过self.initial(request, *args, **kwargs)三大认证模块,执行了认证,频率,权限
self.initial(request, *args, **kwargs)
8.2判断转为小写的request.method请求是否在八个请求内
if request.method.lower() in self.http_method_names:
8.2.1如果在通过反射获取视图类中与之相对于的属性或方法
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
8.3 执行后并赋值给response
response = handler(request, *args, **kwargs)
except Exception as exc:
8.4 如果出现异常,捕获异常,处理异常,正常返回
response = self.handle_exception(exc)
9.最后正常返回
return self.response
2.1 父类(APIView)中的as_view
class APIView(View):
@classmethod
def as_view(cls, **initkwargs):
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation
# 调用了APIView父类View的as_view方法
view = super().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# 通过csrf_exempt取消了csrf认证
# 以后所有的请求,都没有csrf认证了,只要继承了APIView就没有csrf的校验了
return csrf_exempt(view)
2.2 父类APIView的父类(View)的as_view
class View:
@classonlymethod
def as_view(cls, **initkwargs):
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
# 执行view
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
# 返回self.dispatch(request, *args, **kwargs)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
update_wrapper(view, cls, updated=())
update_wrapper(view, cls.dispatch, assigned=())
return view # 返回View类中as_view方法内的view闭包函数
2.3 父类APIView中的dispatch方法
class APIView(View):
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# 调用initialize_request方法将原生的django的request对象重新封装成一个新的request对象
request = self.initialize_request(request, *args, **kwargs)
# self 是视图类的对象,视图类对象.request=request 新的request
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
# 执行了认证,频率,权限
self.initial(request, *args, **kwargs)
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
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(request,parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
3. Request类源码分析
# 视图类中使用request对象,已变成了drf提供的Request类的对象了
--原生django的request是这个类的对象:django.core.handlers.wsgi.WSGIRequest
--drf的request是这个类的对象:rest_framework.request.Request
# request已经不是原来的request了,用起来跟之前一样
# Request的源码分析:rest_framework.request.Request
--类中有个魔法方法:__getattr__ 对象.属性,属性不存在会触发它的执行
def __getattr__(self, attr): # 如果取的属性不存在回去原生django的request对象中取出
try:
# 反射: 根据字符串获取属性或方法,self._request 是愿挨的request
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
--以后用的所有属性或方法,直接用就可以了(通过反射取原来的request中取的)
--新的request内部有个老的request,就是request._request
--以前
urlencoded,form-data提交的数据在request.POST
json格式提交的数据,在request.post中没有,它在request.body中
--data 是个方法,被porperty装饰了,变成了数据属性用
以后无论上述的那种格式,都从request.data中取
from-data提交的数据 在request.data中
<QueryDict: {'name': ['golang'], 'price': ['123'], 'publish': ['北方出版社'], 'author': ['tony123']}>
urlencoded提交的数据,在request.data中
<QueryDict: {'name': ['linux'], 'price': ['8888'], 'publish': ['东方出版社'], 'author': ['tony456']}>
json格式提交的数据,在request.data中
{'name': 'mysql', 'price': '9999', 'publish': '上海出版社', 'author': 'tony789'}
--query_params:get请求提交的参数,等同于request._request.GET或request.GET
--其它:取文件也是从request.FILES中取,跟之前一样。
# 验证
原生request.POST 只有urlencoded和form-data格式提交的数据。
json格式提交的数据在body中,需要自己拿出来自己处理
但drf的request中有个data
data中可以渠道任意编码提交的数据
request.data 有时候是urlencoded,from-data取出来的是QueryDict
有时候json 其取出来的直接是字典形式
# 什么是魔法方法
在类中只要以__开头,__结尾的都称之魔法方法
这种方法不需要手动调用,某种情况下会自动触发
4. 序列化组件介绍
序列化组件时drf提供的一个类,继承它写自己的类
通过它来序列化qs或单个对象
drf提供了一种可以快速实现序列化类:
4.1 定义一个序列化类
models.py
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
author = models.CharField(max_length=32)
publish = models.CharField(max_length=32)
# 新建一个py文件serializer,写序列化类:给book进行序列化
serializer.py
from rest_framework import serializer
class BookSerializer(serializer.Serializer):
"""继承Serializer"""
# 写需要序列化的字段,有很多字段类,字段类又很多的字段属性
name = serializer.CharField()
price = serializer.CharField()
publish = serializer.CharField()
author = serializer.CharFiels()
4.2 序列化类,序列多条数据
views.py
from app01 import serializer
class BookView(APIView):
def get(self, request):
book_list = models.Book.objects.all()
# instance 表示要序列化的数据
# many=True表示序列化多条
ser = serializer.BookSerializer(
instance=book_list, many=True
) # instance时qs对象,一定要传many=True
return Response(ser.data)
4.3 序列化类,序列化单条
urls.py
path('books/<int:pk>', views.BookOneView.as_view()),
views.py
from app01 import serializer
class BookOneView(APIView):
def get(self, request, pk):
book_obj = models.Book.objects.filter(pk=pk).first()
ser = serializer.BookSerializer(instance=book_obj)
return Response(ser.data)
5. 反序列化(新增,修改,删除)
5.1 新增数据
serializer.py 序列化类中
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
author = serializers.CharField()
# 重写create方法
def create(self, validated_data):
res = models.Book.objects.create(**validated_data)
return res
views.py 视图类中
class BookView(APIView):
def get(self, request):
book_list = models.Book.objects.all()
# instance 表示要序列化的数据
# many=True表示序列化多条
ser = serializer.BookSerializer(
instance=book_list, many=True
) # instance时qs对象,一定要传many=True
return Response(ser.data)
def post(self, request):
# 前端传递的数据,从request.data中取出来
ser = serializer.BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
5.2 修改数据
serializer.py 序列化类中
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
author = serializers.CharField()
# 重写update
def update(self, instance, validated_data):
# instance需要修改的对象
# validated_data 校验过后的对象
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish = validated_data.get('publish')
instance.author = validated_data.get('author')
instance.save()
# 最后将修改后的对象返回出去
return instance
views.py 视图类中
class BookOneView(APIView):
def get(self, request, pk):
book_obj = models.Book.objects.filter(pk=pk).first()
ser = serializer.BookSerializer(instance=book_obj)
return Response(ser.data)
def put(self, request, pk):
# 将前端传来的数据赛选出需要修改的对象
book_obj = models.Book.objects.filter(pk=pk).first()
# instance=book_obj, 表示需要修改的对象,
# data=request.data 表示需要修改的数据
ser = serializer.BookSerializer(instance=book_obj, data=request.data)
# 调用save会触发BookSerializer的save方法,判断了,如果instance有值执行update,没有值执行create
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)