61、Request类源码分析、序列化组件、序列化类的基本使用、常用字段类和参数、反序列化之校验、保存

127 阅读11分钟

今日内容概要

  • 作业讲解
  • Request类源码分析
  • 序列化组件介绍
  • 序列化类的基本使用
  • 常用字段类和参数
  • 反序列化之校验
  • 反序列化之保存

作业讲解

写一个装饰器,装饰在fbv上,这个fbv可以接受前端的编码格式可以是urlencoded,form-data,json,取数据,都是从request.data中取

from django.http import JsonResponse
import json

def outer(func):
    def inner(request,*args,**kwargs):
        """
        urlencoded:request.POST
        form-data:request.POST
        json:request.body
        """
        try:
            request.data = json.loads(request.body)  # # 如果是json格式,就成功;如果是其他格式,直接就报错了
        except Exception :
            request.data = request.POST
        res = func(request, *args, **kwargs)
        return res
    return inner


@outer
def index(request):
    if request.method == "POST":
        print(request.data)  # <QueryDict: {'name': ['西游记'], 'price': ['99']}>
        print(request.FILES)  # <MultiValueDict: {'file': [<InMemoryUploadedFile: 2.2.png (image/png)>]}>
        return JsonResponse({'code':200,'msg':'ok','data':{'name':'nana','age':18}})

Request类源码分析

利用APIView+Response写接口

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book

class BookViews(APIView):
    def get(self,request):
        # 需求获取所有书
        book_list = Book.objects.all()
        l1 = []
        for book in book_list:
            dic = {'name':book.name,'price':book.price}
            l1.append(dic)
        return Response({'code':200,'msg':'查询成功','data':l1})
    def post(self,request):
        # 需求创建一本书
        print(request.data)  # <QueryDict: {'name': ['三国演义'], 'price': ['99']}>
        name = request.data.get('name')
        price = request.data.get('price')
        Book.objects.create(name=name,price=price)
        return Response({'code':200,'msg':'创建成功','data':{'name': name, 'price':price}})
      
      
class BookDetails(APIView):
    def get(self, request, pk):
        # 需求    获取主键为1
        book_obj = Book.objects.filter(pk=pk).first()
        return Response({'code': 200, 'msg': '查询成功', 'data': {'name': book_obj.name, 'price': book_obj.price}})

    def put(self,request,pk):
        # 需求修改数据
        print(request.data)  # {'name': 'xiyouji', 'price': 666}
        name = request.data.get('name')
        price = request.data.get('price')
        Book.objects.filter(pk=pk).update(name = name,price = price)
        return Response({'code': 200, 'msg': '修改成功', 'data': {'name': name, 'price': price}})
    def delete(self,request,pk):
        # 需求:删除数据
        Book.objects.filter(pk=pk).delete()
        return Response({'code': 200, 'msg': '删除成功'})

Request类源码分析

1.根据APIView源码的分析:
		当请求来了,会执行url中配置的:path('books/', BookView.as_view()),---->BookView.as_view()(request)---->APIView的as_view类方法---》内部调用的还是View的as_view--->self.dispatch---->APIView的dispatch
  def dispatch(self, request, *args, **kwargs):
        # request新的(rest_framework.request.Request), request老的(django.core.handlers.wsgi.WSGIRequest)
        request = self.initialize_request(request, *args, **kwargs)

2.鼠标点:initialize_request,该函数返回Request方法
    def initialize_request(self, request, *args, **kwargs):
        parser_context = self.get_parser_context(request)
        return Request(
            request,  # 旧的request
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

3.鼠标点:返回值Request()方法
	class Request:
      def __init__(self, request, ...)
        self._request = request   # 旧的request
				pass
      
      @property
      def data(self):
          if not _hasattr(self, '_full_data'):
              self._load_data_and_files()
          return self._full_data
        
      @property
      def query_params(self):
          return self._request.GET
        
			def __getattr__(self, attr):   # request._request.method
        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)
           

总结

1.新的request有个data属性,以后只要是在请求body体中的数据,无论什么编码格式,无论什么请求方式
	ps:1.新的request类型:rest_framework.request.Request
  	 2.旧的request类型:django.core.handlers.wsgi.WSGIRequest
     3.request.data里面还可以有文件,但不要在里面取,还是用request.FILES
2.取文件还是从:request.FILES

3.取其他属性,跟之前完全一样 request.method  ....
    	-原理是:新的Request重写了__getattr__,通过反射获取老的request中的属性
  
4.request.GET == >request.query_params
		@property
		def query_params(self):
    		return self._request.GET
      
      """
			1.补充知识:魔法方法之 __getattr__
			__getattr__:是点拦截(对象.属性),当属性不存在的时候,会自动触发类中的__getattr__
			
			__getattribute__:对象获取属性的时候自动触发,无论这个属性存不存在。当类中既有getattr,又有getattribute的时候,只会执行getattribute
			ps:https://juejin.cn/post/7126535355590770718
			
			2.get请求能不能在body体中带数据--->能
      """

序列化组件介绍

1.功能--3个(序列化,反序列化、校验功能)
    1. 序列化,序列化器会把模型对象(queryset,单个对象)转换成字典,经过response以后变成json字符串
    2. 反序列化,把客户端发送过来的数据,经过request.data以后变成字典,序列化器可以把字典转成模型
    3. 反序列化,完成数据校验功能

序列化类的基本使用

案例

创建book表模型-->APIView+Response+序列化 写接口

需求:查询单条和所有数据

Views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializar import BookSerializer


class BookViews(APIView):
    def get(self,request):
        # 需求获取所有书
        book_list = Book.objects.all()
        """
        参数:instance--->实例,对象, data=empty--->前端传入的数据
        many=True --->多条数据,如果是queryset对象,就要写
        many=False--->单个对象,默认的
        """
        # 使用序列化类,完成序列化
        ser = BookSerializer(instance=book_list,many=True)
        print(ser)
        """
        BookSerializer(instance=<QuerySet [<Book: Book object (2)>, <Book: Book object (3)>]>, many=True):
        id = IntegerField(required=False)
        name = CharField()
        price = IntegerField()
        """
        print(ser.data)  # [OrderedDict([('id', 2), ('name', '红楼梦'), ('price', 99)]), OrderedDict([('id', 3), ('name', '三国演义'), ('price', 99)])]

        print(type((ser.data)))  # <class 'rest_framework.utils.serializer_helpers.ReturnList'>
        return Response({'code':200,'msg':'查询成功','data':ser.data})

class BookDetails(APIView):
    def get(self, request, pk):
        # 需求    获取主键为1
        book_obj = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book_obj)

        return Response({'code': 200, 'msg': '查询成功', 'data': ser.data})

serializer.py

from rest_framework import serializers
class BookSerializer(serializers.Serializer):
    # 要序列化的字段
    id = serializers.IntegerField(required=False)  # 前端传入数据,可以不填这个字段
    name = serializers.CharField()
    price = serializers.IntegerField()

urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookViews.as_view()),
    path('books/<int:pk>/', views.BookDetails.as_view()),
]

总结

# 序列化类的使用
  1.写一个类,继承serializers.Serializer
  2.在类中写字段,要序列化的字段
  	eg: name = serializers.CharField()
  3.在视图类中使用:(多条,单条)
      serializer = BookSerializer(instance=book_list, many=True)
      serializer = BookSerializer(instance=book)

常用字段类和参数

常见字段类

字段字段构造方式
BooleanFieldBooleanField()
NullBooleanFieldNullBooleanField()
CharFieldCharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailFieldEmailField(max_length=None, min_length=None, allow_blank=False)
RegexFieldRegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugFieldSlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLFieldURLField(max_length=200, min_length=None, allow_blank=False)
UUIDFieldUUIDField(format=’hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressFieldIPAddressField(protocol=’both’, unpack_ipv4=False, **options)
IntegerFieldIntegerField(max_value=None, min_value=None)
FloatFieldFloatField(max_value=None, min_value=None)
DecimalFieldDecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeFieldDateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateFieldDateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeFieldTimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationFieldDurationField()
ChoiceFieldChoiceField(choices) choices与Django的用法相同
MultipleChoiceFieldMultipleChoiceField(choices)
FileFieldFileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageFieldImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListFieldListField(child=, min_length=None, max_length=None)
DictFieldDictField(child=)
1.常用的:IntegerField   CharField   DateTimeField  DecimalField
2.ListField和DictField--->比较重要,后面了解

参数(用来数据校验)

选项参数:(CharField,IntegerField)

参数名称作用
max_length最大长度
min_lenght最小长度
allow_blank是否允许为空
trim_whitespace是否截断空白字符
max_value最小值
min_value最大值

通用参数:

参数名称说明
read_only表明该字段仅用于序列化输出,默认False
write_only表明该字段仅用于反序列化输入,默认False
required表明该字段在反序列化时必须输入,默认True
default反序列化时使用的默认值
allow_null表明该字段是否允许传入None,默认False
validators该字段使用的验证器
error_messages包含错误编号与错误信息的字典
label用于HTML展示API页面时,显示的字段名称
help_text用于HTML展示API页面时,显示的字段帮助提示信息

ps:read_only 、write_only 很重要

反序列化

反序列化之校验

1.反序列化有三层校验
    1.字段自己的(写的字段参数:required   max_length 。。。)
    2.局部钩子:写在序列化类中的方法,方法名必须是 validate_字段名
    	def validate_name(self, name):
            if 'sb' in name:
                # 不合法,抛异常
                raise ValidationError('书名中不能包含sb')
            else:
                return name
    3.全局钩子:写在序列化类中的方法 方法名必须是 validate
       def validate(self, attrs):
            price = attrs.get('price')
            name = attrs.get('name')
            if name == price:
                raise ValidationError('价格不能等于书名')
            else:
                return attrs

字段自己的

      from rest_framework import serializers
      class BookSerializer(serializers.Serializer):
        # 要序列化的字段
        id = serializers.IntegerField(required=False)  # 前端传入数据,可以不填这个字段
        name = serializers.CharField(max_length=8)
        price = serializers.IntegerField(min_value=50,error_messages='价格不能低于50')
     
    
    # view.py
    def post(self,request):
      # 需求创建一本书
      # 1.前端会传入数据,request.data--->把这个数据保存到数据库中--->借助于序列化类,完成 校验和反序列化
      ser = BookSerializer(data=request.data)
      # 校验数据
      if ser.is_valid():
        print(ser.validated_data)
        return Response({'code': 200, 'msg': '创建成功'})  # OrderedDict([('name', '三国演义'), ('price', 99)])
      else:
        print(ser.errors)
        return Response({'code':200,'msg':'创建失败','errors':ser.errors})  
      """
      结果:
        {
      "code": 200,
      "msg": "创建失败",
      "errors": {
          "price": [
              "Ensure this value is greater than or equal to 50."
          ]
      }
  }
      """
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
    # 要序列化的字段
    id = serializers.IntegerField(required=False)  # 前端传入数据,可以不填这个字段
    name = serializers.CharField(max_length=8,allow_blank=True, required=False)
    price = serializers.IntegerField(min_value=50,max_value=100,error_messages={'min_value':'价格不能低于50','max_value':'最高不超过100'})

局部钩子(给某个字段做验证)

# serializer.py
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.Serializer):
    # 要序列化的字段
    id = serializers.IntegerField(required=False)  # 前端传入数据,可以不填这个字段
    name = serializers.CharField(max_length=8)
    price = serializers.IntegerField(min_value=50,max_value=100,error_messages={'min_value':'价格不能低于50','max_value':'最高不超过100'})

    def validate_name(self,name):
        # 需求:名字中不包含sb
        if 'sb' in name:
            raise ValidationError('不能含有sb')
        else:
            return name

    def validate_price(self,price):
        if price == 88:
            raise ValidationError('价格能为88')
        else:
            return price
          
          
          
# views.py
def post(self,request):

  # 需求创建一本书
  # 1.前端会传入数据,request.data--->把这个数据保存到数据库中--->借助于序列化类,完成 校验和反序列化
  ser = BookSerializer(data=request.data)
  # 校验数据
  if ser.is_valid():
    print(ser.validated_data)
    return Response({'code': 200, 'msg': '创建成功'})  # OrderedDict([('name', '三国演义'), ('price', 99)])
  else:
    print(ser.errors)
    return Response({'code':200,'msg':'创建失败','errors':ser.errors})
  
  
  """
  {
    "code": 200,
    "msg": "创建失败",
    "errors": {
        "name": [
            "Ensure this field has no more than 8 characters."
        ],
        "price": [
            "价格能为88"
        ]
    }
}
  """

全局钩子

def validate(self, attrs):
    price = attrs.get('price')
    name = attrs.get('name')
    if name == price:
      raise ValidationError('价格不能等于书名')
    else:
      return attrs

反序列化之保存

新增接口

1.序列化类的对象,实例化的时候:ser = BookSerializer(data=request.data)
2.数据校验过后---->调用  序列化类.save()--->但是要在序列化类中重写  create方法
    def create(self, validated_data):
        book=Book.objects.create(**validated_data)
        return book

修改接口

1.序列化类的对象,实例化的时候:
			ser = BookSerializer(instance=book,data=request.data)
2.数据校验过后----》调用  序列化类.save()--->但是要在序列化类中重写  update方法
  
  def update(self, book, validated_data):
        # instance 要修改的对象
        # validated_data:前端传入,并且校验过后的数据
        # book.name = validated_data.get('name')
        # book.price = validated_data.get('price')
        
        # 优化
        for  item in validated_data:
            setattr(book,item,validated_data[item])
        book.save()
        return book

案例

urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookViews.as_view()),
    path('books/<int:pk>/', views.BookDetails.as_view()),
]

Serializer.py

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Book
class BookSerializer(serializers.Serializer):
    # 要序列化的字段
    id = serializers.IntegerField(required=False)  # 前端传入数据,可以不填这个字段
    name = serializers.CharField(max_length=8)
    price = serializers.IntegerField(min_value=50,max_value=100,error_messages={'min_value':'价格不能低于50','max_value':'最高不超过100'})

    def validate_name(self,name):
        # 需求:名字中不包含sb
        if 'sb' in name:
            raise ValidationError('不能含有sb')
        else:
            return name

    def validate_price(self,price):
        if price == 88:
            raise ValidationError('价格能为88')
        else:
            return price

    def create(self, validated_data):  # validated_data:校验过后的数据

        book=Book.objects.create(**validated_data)
        return book

    def update(self, book, validated_data):
        # instance 要修改的对象
        # validated_data:前端传入,并且校验过后的数据
        # book.name = validated_data.get('name')
        # book.price = validated_data.get('price')

        # 优化
        for  item in validated_data:
            setattr(book,item,validated_data[item])
        book.save()
        return book

Views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializar import BookSerializer


class BookViews(APIView):
    def get(self,request):
        # 需求获取所有书
        book_list = Book.objects.all()
        """
        参数:instance--->实例,对象, data=empty--->前端传入的数据
        many=True --->多条数据,如果是queryset对象,就要写
        many=False--->单个对象,默认的
        """
        # 使用序列化类,完成序列化
        ser = BookSerializer(instance=book_list,many=True)
        print(ser)
        """
        BookSerializer(instance=<QuerySet [<Book: Book object (2)>, <Book: Book object (3)>]>, many=True):
        id = IntegerField(required=False)
        name = CharField()
        price = IntegerField()
        """
        print(ser.data)  # [OrderedDict([('id', 2), ('name', '红楼梦'), ('price', 99)]), OrderedDict([('id', 3), ('name', '三国演义'), ('price', 99)])]

        print(type((ser.data)))  # <class 'rest_framework.utils.serializer_helpers.ReturnList'>
        return Response({'code':200,'msg':'查询成功','data':ser.data})
    def post(self,request):
        # 需求创建一本书
        # 1.前端会传入数据,request.data--->把这个数据保存到数据库中--->借助于序列化类,完成 校验和反序列化
        ser = BookSerializer(data=request.data)
        # 校验数据
        if ser.is_valid():  # 三层校验通过
            print(ser.validated_data)  # validated_data:校验过后的数据
            # 保存数据
            ser.save()
            return Response({'code': 200, 'msg': '创建成功'})  # OrderedDict([('name', '三国演义'), ('price', 99)])
        else:
            print(ser.errors)
            return Response({'code':200,'msg':'创建失败','errors':ser.errors})
class BookDetails(APIView):
    def get(self, request, pk):
        # 需求    获取主键为1
        book_obj = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book_obj)
        return Response({'code': 200, 'msg': '查询成功', 'data': ser.data})

    def put(self,request,pk):
        book_obj = Book.objects.filter(pk=pk).first()
        # 需求修改数据
        ser = BookSerializer(instance=book_obj,data=request.data)
        if ser.is_valid():
            print(ser.validated_data)  # OrderedDict([('name', 'xiyouji'), ('price', 89)])
            ser.save()
            return Response({'code': 200, 'msg': '修改成功'})
        else:
            print(ser.errors)
            return Response({'code': 200, 'msg': '修改失败', 'errors':ser.errors})
    def delete(self,request,pk):
        # 需求:删除数据
        Book.objects.filter(pk=pk).delete()
        return Response({'code': 200, 'msg': '删除成功'})

作业

1.Publish的5个接口  
		-name
    -addr
    -phone
    
  1.出版社名字不能包含东京,最大长度不能超过10,最短不能短于2  
  2.地址长度不能超过20,没有最短现在,地址不能以上海开头
  3.手机号必须114.出版社名字和地址名字不能一样
    """
    1.自己写的
      name--->max_length = 10,min_length=2
      addr--->max_length = 20,

    2.局部钩子
      name--->validate_name--->in
      addr--->validate_addr--->startswith
      phone--->validate_phone--->len()
    3.全局钩子
      validate---->name != addr  
    """

urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('publish/',views.PublishView.as_view() ),
path('publish/<int:pk>/',views.PublishDetail.as_view() ),
]

Serializer.py

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Publish

class PublishSerializer(serializers.Serializer):
    # id = serializers.IntegerField(required=False)
    name = serializers.CharField(max_length=10,min_length=2,error_messages={'max_length':'名字最大不能10位','min_length':'名字最小不能2位'})
    addr = serializers.CharField(max_length=20,error_messages={'max_length':'地址最大20位'})
    phone = serializers.CharField()

    def validate_name(self,name):
        if '东京' in name:
            raise ValidationError('出版社名字不能包含东京')
        else:
            return name

    def validate_addr(self,addr:str):
        if addr.startswith('上海'):
            raise ValidationError('出版社地址不能以上海开头')
        else:
            return addr
    def validate_phone(self,phone):
        if len(phone) !=11:
            raise ValidationError('出版社电话必须11位')
        else:
            return phone

    def validate(self, attrs):
        # print(attrs)
        name = attrs.get('name')
        addr = attrs.get('addr')
        if name == addr:
            raise ValidationError('出版社的地址和名字不能一样')
        else:
            return attrs

    def create(self, validated_data):
        publish = Publish.objects.create(**validated_data)
        return publish

    def update(self, publish, validated_data):
        for item in validated_data:
            setattr(publish,item,validated_data[item])
        publish.save()
        return publish



views.py

from django.shortcuts import render

# Create your views here.

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Publish
from .serializer import PublishSerializer


class PublishView(APIView):

    def get(self,request):
        publish_list = Publish.objects.all()
        ser = PublishSerializer(instance=publish_list,many=True)
        return Response({'code':200,'msg':'查询成功','data':ser.data})

    def post(self,request):
        # 新增出版社
        ser = PublishSerializer(data=request.data)
        # 校验数据
        if ser.is_valid():
            print(ser.validated_data)
            ser.save()
            return Response({'code': 200, 'msg': '添加成功', 'data': ser.data})
        else:
            print(ser.errors)
            return Response({'code': 200, 'msg': '添加失败', 'errors_msg':ser.errors})



class PublishDetail(APIView):
    def get(self,request,pk):
        publish_obj = Publish.objects.filter(pk=pk).first()
        ser = PublishSerializer(instance=publish_obj)
        return Response({'code': 200, 'msg': '查询成功', 'data': ser.data})

    def put(self,request,pk):
        publish_obj = Publish.objects.filter(pk=pk).first()
        # 修改数据
        ser = PublishSerializer(instance=publish_obj,data=request.data)
        # 校验数据
        if ser.is_valid():
            print(ser.validated_data)
            ser.save()
            return Response({'code': 200, 'msg': '修改成功', 'data': ser.data})
        else:
            print(ser.errors)
            return Response({'code': 200, 'msg': '修改失败', 'errors_msg': ser.errors})

    def delete(self,request,pk):
        Publish.objects.filter(pk =pk).delete()
        return Response({'code': 200, 'msg': '删除成功'})