rest-framework
1.安装
方式一:pip3 install djangorestframework
方式二:pycharm图形化界面安装
方式三:pycharm命令行下安装(装在当前工程所用的解释器下)
2.djangorestframework的APIView分析(详见源码)
在视图函数中定义一个类(Book)继承了APIview,然后在url中,view.Book.as_view(),进入这个as_view(),在140行可以看到view = super(APIView, cls).as_view(**initkwargs)
,然后执行了父类也就是View的as_view()方法,点进去看,然后在68行有个return self.dispatch(request, *args, **kwargs)
,记住此时的self指的是实例化的对象,对象的方法应该从头开始找,会找到APIview里的dispatch方法,而不是直接找View里的dispatch方法。然后在478行执行了request = self.initialize_request(request, *args, **kwargs)
,然后继续找initialize_request方法,还是在APIview里面。然后在377行返回了一个Request对象。这里的Request类是rest_framework自己封装的类,通过这个类实例化出一个对象再一步步返回回去。
3.django的rest_framework的Request对象简单介绍
详见上图及源码
4.Django自带序列化组件
from django.core import serializers
def test(request):
book_list = Book.objects.all()
ret = serializers.serialize("json", book_list)
return HttpResponse(ret)
5. rest-framework序列化之Serializer,ModelSerializer,请求数据校验和保存功能
from django.db import models
# Create your models here.
class Book(models.Model):
title = models.CharField(max_length=32,null=True)
price = models.IntegerField()
pub_date = models.DateField(auto_now_add=True)
publish = models.ForeignKey(to='Publish',null=True)
author = models.ManyToManyField(to='Author')
def __str__(self):
return self.title
def test(self):
return '555'
class Publish(models.Model):
name = models.CharField(max_length=32)
email = models.EmailField()
# def __str__(self):
# return self.name
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
from django.shortcuts import render,HttpResponse,redirect
import json
from django.views import View
from django.core import serializers
from app01 import models
from django.http import JsonResponse
# Create your views here.
# def book(request):
# if request.method == 'GET':
# dic = {
# 'status':1,
# 'title':'金瓶梅',
# 'price':100
# }
# return HttpResponse(json.dumps(dic))
# class Book(View):
# def get(self,request):
# # dic = {
# # 'status': 1,
# # 'title': '金瓶梅',
# # 'price': 100
# # }
# # return HttpResponse(json.dumps(dic))
# # 方式一
# # res = models.Book.objects.all()
# # l = []
# # for book in res:
# # l.append({'title':book.title})
# # return HttpResponse(json.dumps(l))
# # 方式二
# # res = models.Book.objects.all().values('title')
# # return HttpResponse(json.dumps(list(res)))
# # 方式三
# book_list = models.Book.objects.all()
# res = serializers.serialize('json',book_list)
# return HttpResponse(res)
#
# def post(self,request):
# return HttpResponse('ok')
# 用APIView
from rest_framework import serializers
from rest_framework.response import Response
'''
方法一
class AuthorSerializer(serializers.Serializer):
name = serializers.CharField()
age = serializers.IntegerField()
class BookSerializer(serializers.Serializer):
title = serializers.CharField()
# 如果想重命名,指定source
# xx = serializers.CharField(source='title')
price = serializers.IntegerField()
pub_date = serializers.DateField()
# publish = serializers.CharField()
publish = serializers.CharField(source='publish.email')
# author = serializers.CharField(source='author.all')
author = serializers.SerializerMethodField() # 指向一个字段的方法,该方法的返回值指向该字段
# 这里的函数名必须是"get_" 加字段名,
def get_author(self,obj):
# 方式一:
# print(obj) # 这里的obj指的是每一本书这个对象
# l = []
# for author in obj.author.all():
# l.append(author)
# return l
# 方式二:
res = obj.author.all()
author_ser = AuthorSerializer(res,many=True)
return author_ser.data
from rest_framework.views import APIView
class Book(APIView):
# 这里的这个request指的就是rest_framework自己封装的request
# 从源码可以找出request._request是原来django封装的那个
def get(self,request):
# print(request) ===><rest_framework.request.Request object at 0x00000256C223B9E8>
# print(request._request) ===><WSGIRequest: GET '/book/'>
res = models.Book.objects.all()
# ret是queryset对象,many=True代表序列化多条,many=False代表序列化一条
book_ser = BookSerializer(res,many=True)
print(book_ser.data) # 是对象转成的字典
return Response(book_ser.data)
# return HttpResponse(json.dumps(list(res)))
def post(self,request):
# 无论通过urlencoded还是json格式编码传来的数据都是被rest_framework处理好了放在data里,是一个字典
print(request.data) # {'title': 'aaaa'}
return HttpResponse('ok')
'''
'''
# 方法二
class AuthorSerializer(serializers.Serializer):
name = serializers.CharField()
age = serializers.IntegerField()
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book # 指定哪张表
# 注意,如果写__all__代表全取,跟exclude不能同时用
fields = '__all__' # 指向那张表哪几个字段
# fields = ('id','publish')
# exclude=('id','publish') # 去除哪个字段
# 可以重写某个字段
# id = serializers.CharField(source='test')
title = serializers.CharField(error_messages={'required':'标题必填'})
# author = serializers.SerializerMethodField()
# def get_author(self,obj):
# res = obj.author.all()
# author_ser = AuthorSerializer(res,many=True)
# return author_ser.data
from rest_framework.views import APIView
class Book(APIView):
# 这里的这个request指的就是rest_framework自己封装的request
# 从源码可以找出request._request是原来django封装的那个
def get(self,request):
# print(request) ===><rest_framework.request.Request object at 0x00000256C223B9E8>
# print(request._request) ===><WSGIRequest: GET '/book/'>
res = models.Book.objects.all()
# ret是queryset对象,many=True代表序列化多条,many=False代表序列化一条
book_ser = BookSerializer(res,many=True)
# print(book_ser.data) # 是对象转成的字典
return Response(book_ser.data)
# return HttpResponse(json.dumps(list(res)))
def post(self,request):
# 无论通过urlencoded还是json格式编码传来的数据都是被rest_framework处理好了放在data里,是一个字典
# print(request.data) # {'title': 'aaaa'}
# print(request.POST)
# Serializer的反序列化功能
ser = BookSerializer(data=request.data)
# 字段校验功能
if ser.is_valid():
# 保存到数据库
ser.save()
return HttpResponse('ok')
else:
print(ser.errors)
return JsonResponse(ser.errors)
'''
6.局部钩子及全局钩子分析
# 局部钩子函数源码分析
#is_valid---->self.run_validation-(执行Serializer的run_validation)-->self.to_internal_value(data)-->(执行Serializer的run_validation:485行)
from rest_framework.exceptions import ValidationError
class AuthorSerializer(serializers.Serializer):
name = serializers.CharField()
age = serializers.IntegerField()
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book # 指定哪张表
# 注意,如果写__all__代表全取,跟exclude不能同时用
fields = '__all__' # 指向那张表哪几个字段
title = serializers.CharField(error_messages={'required':'标题必填'})
# 局部钩子使用
def validate_title(self, value):
print(value)
# raise ValidationError('就是要报错')
return value
# 全局钩子使用
# def validate(self, attrs):
# print('>>>>>',attrs) # 是个字典
# if attrs.get('title') == attrs.get('price'):
# return attrs
# else:
# raise ValidationError('书的名字和价格不一样')
from rest_framework.views import APIView
class Book(APIView):
def get(self,request):
res = models.Book.objects.all()
book_ser = BookSerializer(res,many=True)
return Response(book_ser.data)
def post(self,request):
ser = BookSerializer(data=request.data)
# 字段校验功能
if ser.is_valid():
# 保存到数据库
ser.save()
return HttpResponse('ok')
else:
print(ser.errors)
return JsonResponse(ser.errors)
7.解析器
# 解析器的作用
# 限制前端向后端传的数据的格式
from rest_framework.parsers import JSONParser,FormParser,FileUploadParser
# JSONParser ==>解析json格式
# FormParser ==>解析urlencoded格式
class Book(APIView):
# 局部使用解析器
parser_classes = [JSONParser,FormParser]
def get(self,request):
res = models.Book.objects.all()
book_ser = BookSerializer(res,many=True)
return Response(book_ser.data)
def post(self,request):
print(request.POST)
print(request.data)
return HttpResponse('OK')
settings.py配置文件
# 全局配置解析器
REST_FRAMEWORK ={
'DEFAULT_PARSER_CLASSES':['rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsersFileUploadParser',
]
}
7.1 解释器源码分析
在调用request.data时,才进行解析,由此入手
@property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
查看self._load_data_and_files()方法---->self._data, self._files = self._parse()
def _parse(self):
#用户请求头里content_type的值
media_type = self.content_type
#self.parsers 就是用户配置的parser_classes = [FileUploadParser,FormParser ]
#self里就有content_type,传入此函数
parser = self.negotiator.select_parser(self, self.parsers)
查看self.negotiator.select_parser(self, self.parsers)
def select_parser(self, request, parsers):
#同过media_type和request.content_type比较,来返回解析器,然后调用解析器的解析方法
#每个解析器都有media_type = 'multipart/form-data'属性
for parser in parsers:
if media_type_matches(parser.media_type, request.content_type):
return parser
return None
最终调用parser的解析方法来解析parsed = parser.parse(stream, media_type, self.parser_context)
Request实例化,parsers=self.get_parsers()
Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
get_parsers方法,循环实例化出self.parser_classes中类对象
def get_parsers(self):
return [parser() for parser in self.parser_classes]
self.parser_classes 先从类本身找,找不到去父类找即APIVIew 中的
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
api_settings是一个对象,对象里找DEFAULT_PARSER_CLASSES属性,找不到,会到getattr方法
def __getattr__(self, attr):
if attr not in self.defaults:
raise AttributeError("Invalid API setting: '%s'" % attr)
try:
#调用self.user_settings方法,返回一个字典,字典再取attr属性
val = self.user_settings[attr]
except KeyError:
# Fall back to defaults
val = self.defaults[attr]
# Coerce import strings into classes
if attr in self.import_strings:
val = perform_import(val, attr)
# Cache the result
self._cached_attrs.add(attr)
setattr(self, attr, val)
return val
user_settings方法 ,通过反射去setting配置文件里找REST_FRAMEWORK属性,找不到,返回空字典
@property
def user_settings(self):
if not hasattr(self, '_user_settings'):
self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
return self._user_settings
8.补充
__getattr__ 拦截点号运算。当对未定义的属性名称和实例进行点号运算时,就会用属性名作为字符串调用这个方法。如果继承树可以找到该属性,则不调用此方法。
__setattr__会拦截所有属性的的赋值语句。如果定义了这个方法,self.arrt = value 就会变成self,__setattr__("attr", value).这个需要注意。当在__setattr__方法内对属性进行赋值是,不可使用self.attr = value,因为他会再次调用self,__setattr__("attr", value),则会形成无穷递归循环,最后导致堆栈溢出异常。应该通过对属性字典做索引运算来赋值任何实例属性,也就是使用self.__dict__['name'] = value