DRF之APIView

APIView执行流程

基于APIView+JsonResponse编写接口

原来基于django原生的view编写接口
drf提供给咱们的一个类,以后使用drf写视图类,都是集成这个类及其子类,APIView本身就是集成了Django原生的View

代码展示:
from rest_framework.views import APIView
from .models import Book
from django.http import JsonResponse


class BookView(APIView):

    def get(self, request):
        # 拿到所有的数据
        books = Book.objects.all()
    # 缺点是还要自己for循环放到列表
        book_list = []
        for book in books:
            book_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
     # 序列化不支持列表,需要填写参数
        return JsonResponse(book_list, safe=False)

基于APIView+Response写接口

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


class BookView(APIView):

    def get(self, request):
        # 拿到所有的数据
        books = Book.objects.all()
        book_list = []
        for book in books:
            book_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
        # 相对于JsonResponse来说,不需要再写参数,列表同样可以序列化
        return Response(book_list)

APIView的执行流程

"""
首先复习对象名字的查找顺序,先在对象自己的内部名称空间,然后到产生对象的类,在到产生对象的类的父类中查找,每一次查找名字都是按照这样的顺序去找
"""
路由中写的: path('books/', views.BookView.as_view()),---》请求来了,执行views.BookView.as_view()()----->现在的as_view是APIView的as_view,因为我们自己写的类没有这个as_view这个方法,所以要从父类中寻找,现在我们的父类是APIView


class APIView(View):
    # 这里我们可以看到APIView其实继承的还是django原生的View
    @classmethod  # 这句话标识是绑定给类的方法,那么调用这个as_view会直接将类传入,我们是自己写的类在路由层调用了该方法,所以会将我们自己写的类作为第一个参数传入,也就是参数的cls
    def as_view(cls, **initkwargs):
	# APIView的as_view方法:view还是原来的view,但是以后再也没有csrf认证了
        view = super().as_view(**initkwargs)
     # 上面这句话表示子类调用父类的方法,也就是调用View类的as_view方法,然后拿到了里面的闭包函数view
        view.cls = cls
        view.initkwargs = initkwargs
  # csrf_exempt是装饰器,这样写其实就是装饰器语法糖作用在view这个函数上直接将函数作为参数传入,那么执行view这个函数就全程不会校验scrf了
        return csrf_exempt(view)


# 路由匹配成功,执行csrf_exempt(view)(requets)--->在执行view这个函数也就是在View类中的as_view中的闭包函数view---》这个函数会返回执行self.dispatch---->self是视图类的对象---->BookView---->然后对象点dispatch方法,首先从对象自己找然后再找产生对象的类也就是BookView都没有再从父类里面找也就是APIView里面的dispatch方法,找到了

def dispatch(self, request, *args, **kwargs):
        # request是django原生的request,老的request
        # 把老的request包装成了新的request,这个是drf提供的Request类的对象
        request = self.initialize_request(request, *args, **kwargs)
        # 到此以后,这个request就是新的了,老的request在哪?
        # request._request 这是老的
        
        # 把新的request放到了self对象【BookView的对象】
        self.request = request
        try:
            # 执行了三大认证【认证,频率,权限】,使用新的request,不读
            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
			# 把新的request传入了,视图类的方法中get的request也是新的
            response = handler(request, *args, **kwargs)

        except Exception as exc:
            # 在执行3大认证和视图类中方法的过程中,如果出了异常,都能捕获到---》全局异常捕获
            response = self.handle_exception(exc)
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

    
    总结:APIView的执行流程
    	1.去除了所有的csrf
        2.包装了新的request,以后在视图类中的request是新的request,Request类的对象,不是原生的了
        -原生的在新的request._request
        3.在执行视图类的方法之前,执行了3大认证
        4.如果在3大认证或者视图函数方法执行过程中出了错,就会有异常捕获---》全局异常捕获
        5.以后视图类方法中的request都是新的了

Request对象源码分析

新的Request----》区别于老的
老的:django.core.handlers.wsgi.WSGIRequest  
新的:from rest_framework.request import Request
    新的request._request 就是老的
    
    
Request源码
	-方法 __getattr__
    	-在视图类的方法中,执行request.method,新的request是没有method方法的,就出发了新的Request的__getattr__方法执行
        def __getattr__(self, attr):
            try:
                # 从老的request中反射出 要取得属性
                return getattr(self._request, attr)
            except AttributeError:
                return self.__getattribute__(attr)
 所以我们使用新的request点method的时候其实拿到的是老的request.method的结果


request.data---》是个方法,包装成了数据属性
	--以后无论post、put等放在body中提交的数据,都从request.data中取,取出来的就是字段
    --无论是哪种编码格式

request.query_params---》这是个方法,包装成了数据属性
 	-get请求携带的参数,以后从这里面取
    -query_params:查询参数--->restful规范请求地址中带查询参数
    
-request.FILES--->这是个方法,包装成了数据属性
    	-前端提交过来的文件,从这里取
 
# Request类总结
	1.新的request用起来,跟之前一模一样,因为新的取不到,会自动出发__getattr__取老的对应的方法
    2.request.data 无论什么编码,什么请求方法,只要是body中的数据,就从这里取,字典
    3.request.query_params 就是原来的request._request.GET
    4.上传的文件从request.FILES中取

序列化器介绍和快速使用

因为我们在写接口时,需要序列化和反序列化,而且反序列化的过程中要做数据校验---》drf直接提供了固定的写法,只要按照固定写法使用,就能完成上面的三个需求

# 提供了两个类  Serializer ModelSerializer
	-以后只需要写自己的类,集成drf提供的序列化类,使用其中的某些方法,就能完成上面的操作
    
# 使用APIView+序列化类+Response完成接口的编写

序列化类基本使用,序列化多条

Serializer.py--BookSerializer类

from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 序列化某些字段,这里要写序列化的字段
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()

views.py-->BookView类

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


class BookView(APIView):

    def get(self, request):
        print(request.method)  # GET
        print(request._request.method)   # GET
        print(self.request._request.method)   # GET
        # 拿到所有的数据
        books = Book.objects.all()
        # 使用序列化类来完成序列化
        # instance是要序列化的数据boos queryset对象
        # many=True 只要是queryset对象要穿many=True,如果是单个对象就不用传
        ser = BookSerializer(instance=books,many=True)
        return Response(ser.data)  # 无聊是列表还是字典都可以序列化

序列化单条

序列化类没动

from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 序列化某些字段,这里要写序列化的字段
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()

视图类重新写一个新的

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

class BooksView(APIView):

    def get(self, request, *args, **kwargs):
        book = Book.objects.filter(pk=kwargs.get('pk')).first()
        # 序列化,单条不用写many=True
        ser = BookSerializer(instance=book)
        return Response(ser.data)

url添加新的路由

urlpatterns = [
    path('book/<int:pk>/', views.BooksView.as_view()),
]

反序列化

反序列化的新增

序列化类写一个create方法

    def create(self, validated_data):
        # 保存的逻辑
        # validated_date是校验过后的数据
        # 保存到数据库
        book = Book.objects.create(**validated_data)
        # 一定要返回新增的对象
        return book

视图类

    def post(self, request):
        ser = BookSerializer(data=request.data)  # 把前端传入的要保存的数据吗,给data参数
        # 校验数据
        if ser.is_valid():
            # 保存需要自己写,要在序列化类BookSerializer中写一个create方法
            ser.save()  # 调用ser.save方法自动出发咱们写的create保存数据
            return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
        return Response({'code': 101, 'msg': ser.errors})

序列化的修改

序列化类写一个修改的方法

    def update(self, instance, validated_data):
        # instance要修改的对象,也就是我们数据库中原来的orm对象
        # validated_data前端传过来校验后的数据
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish = validated_data.get('publish')
        instance.save()  # orm的单个对象,修改了单个对象的属性,只要对象调用了save方法,就可以把修改的保存到数据库中
        return instance   # 把修改后的对象返回出去

视图类

    def put(self, request, **kwargs):
        # 获取要修改的数据对象
        book = Book.objects.filter(pk=kwargs.get('pk')).first()
        # 把要修改的数据对象和前端的传来的数据都传给序列化类
        ser = BookSerializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()  # 调用save方法自动调用我们写的update方法
            return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
        return Response({'code': 101, 'msg': ser.errors})

删除单条

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

反序列化的校验

序列化反序列化,数据校验功能----》类比forms组件
局部钩子和全局钩子
from rest_framework import serializers
from .models import Book
from rest_framework.exceptions import ValidationError

class BookSerializer(serializers.Serializer):
    # 序列化某些字段,这里要写序列化的字段
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()

    def create(self, validated_data):
        # 保存的逻辑
        # validated_date是校验过后的数据
        # 保存到数据库
        book = Book.objects.create(**validated_data)
        # 一定要返回新增的对象
        return book

    def update(self, instance, validated_data):
        # instance要修改的对象,也就是我们数据库中原来的orm对象
        # validated_data前端传过来校验后的数据
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish = validated_data.get('publish')
        instance.save()  # orm的单个对象,修改了单个对象的属性,只要对象调用了save方法,就可以把修改的保存到数据库中
        return instance  # 把修改后的对象返回出去

    # 局部钩子
    def validate_name(self, name):
        # 校验name是否合法
        if name.startswith('sb'):
            # 校验不通过,抛异常
            raise ValidationError('不能以sb开头')
        else:
            return name

    # 全局钩子
    def validate(self, attrs):
        # attrs就是前端传来的数据
        if attrs.get('name') == attrs.get('publish'):
            raise ValidationError('书名跟出版社名字不能一致')
        else:
            return attrs

序列化类常用的字段和字段参数

序列化类---》字段类 CharField,除此之外还有不少

序列化类---》字段类,字段类上,传属性的,序列化类上,也可以写属性
类似于:【models.CharField(max_length=32)】

常用字段类

#1 BooleanField       BooleanField()

#2 NullBooleanField   NullBooleanField()

#3 CharField    CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)

#4 EmailField   EmailField(max_length=None, min_length=None, allow_blank=False)

#5 RegexField   RegexField(regex, max_length=None, min_length=None, allow_blank=False)

#6 SlugField    SlugField(max_length=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+

#7 URLField URLField(max_length=200, min_length=None, allow_blank=False)

#8 UUIDField    UUIDField(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"

#9 IPAddressField   IPAddressField(protocol=’both’, unpack_ipv4=False, **options)

#10 IntegerField    IntegerField(max_value=None, min_value=None)

#11 FloatField  FloatField(max_value=None, min_value=None)

#12 DecimalField    DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置

#13 DateTimeField   DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)

#14 DateField   DateField(format=api_settings.DATE_FORMAT, input_formats=None)

#15 TimeField   TimeField(format=api_settings.TIME_FORMAT, input_formats=None)

#16 DurationField   DurationField()

#17 ChoiceField ChoiceField(choices) choices与Django的用法相同

#18 MultipleChoiceField MultipleChoiceField(choices)

#19 FileField   FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)

#20 ImageField  ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)

----------重要,后面讲-----------
ListField   ListField(child=, min_length=None, max_length=None)DictField   DictField(child=)

#记住的:CharField  IntegerField  DecimalField  DateTimeField BooleanFieldListFieldDictField

常用字段参数

选项参数:

# CharField及其子类的(EmailField)---》反序列化的校验,字段自己的规则
max_length 			 最大长度
min_length  		 最小长度
allow_blank 		 是否允许为空
trim_whitespace		 是否截断空白字符

# IntegerField
max_value			最大值
min_value			最小值

# 所有字段类都有的
required				表明该字段在反序列化时必须输入,默认True
default					反序列化时使用的默认值
allow_null				表明该字段是否允许传入None,默认False
validators				该字段使用验证器
----看一眼忘掉-----
error_messages	包含错误编号与错误信息的字典
label	用于HTML展示API页面时,显示的字段名称
help_text	用于HTML展示API页面时,显示的字段帮助提示信息

# 重点:
read_only		表明该字段仅用于序列化输出,默认False
srite_only		表明该字段仅用于反序列化输入,默认False


# 反序列化校验执行流程
	-1.先执行字段自己的校验规则---》最大长度,最小长度,是否为空,是否必填,最小数据。。。
     -2 validators=[方法,] ----》单独给这个字段加校验规则
    	name=serializers.CharField(validators=[方法,])
     -3 局部钩子校验规则
     -4 全部钩子校验规则

序列化高级用法之source

# 创建关联表
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)  # 留住,还有很多
    authors = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.CharField(max_length=11)

# 迁移,录入数据

序列化定制字段名字

# 重点:source可以指定序列化字段的名字
	-自有字段,直接写字段名字
    	-name_real = serializers.CharField(max_length=8, source='name')
    -关联字段,一对多的关联,直接点
    	-publish = serializers.CharField(source='publish.name')
    -多对多,搞不了,source不能用
    	-authors=serializers.CharField(source='authors.all')
        
        
    # 关联字段,一对多的关联,直接点,会直接显示名字
    publish = serializers.CharField(source='publish.name')

    #多对多,搞不了,source不能用,显示不出来
    authors=serializers.CharField(source='authors.all')

序列化高级用法之定制字段的两种方式

SerializerMethodField定制

# 定制关联字段的显示形式
	-一对多,显示字典
    -多对多,显示列表套字典

代码展示:
from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    new_name = serializers.CharField(source='name')
    price = serializers.CharField()
	
    # 定制返回格式---》方式一
    publish_detail = serializers.SerializerMethodField()

    def get_publish_detail(self, obj):
        return {'name': obj.publish.name, 'addr': obj.publish.addr}

    author_list = serializers.SerializerMethodField()

    def get_author_list(self, obj):
        author_list = obj.authors.all()
        return [{'name': author.name, 'phone': author.phone} for author in author_list]

在模型表中定制

# 模型类中:
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    # 外键字段
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    @property  # 利用伪装,可以直接点,返回的还是一样的
    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.addr}

    @property
    def authors_list(self):
        author_list = self.authors.all()
        return [{'name': author.name, 'phone': author.phone} for author in author_list]
    
    
    
    # 序列化类
    class BookSerializer(serializers.Serializer):
    new_name = serializers.CharField(source='name')
    price = serializers.CharField()
    # 这两个就相当于普通的字段直接Book对象序列化时点方法就可以点出来
    publish_detail = serializers.DictField()
    authors_list = serializers.ListField()

多表关联序列化保存

新增图书接口

-前端传入的数据格式:{name:红楼梦,price:19,publish:1,authors:[1,2]}
"""注意字段要跟反序列类中的字段名是一致的"""
# 视图类
class BookView(APIView):
    def post(self, request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '新增成功'})
        else:
            return Response({'code': 101, 'msg': ser.errors})

# 序列化类
class BookSerializer(serializers.Serializer):
    # name和price  既用来序列化,又用来反序列化   即写又读 ,不用加read_only,write_only
    name = serializers.CharField(max_length=8)
    price = serializers.CharField()

    # 只用来做序列化   只读  read_only
    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)

    # 只用来做反序列化  只写  write_only
    publish = serializers.CharField(write_only=True)
    authors = serializers.ListField(write_only=True)

    # 新增要重写create方法
    def create(self, validated_data):
        # validated_data 校验过后的数据,{name:红楼梦,price:19,publish:1,authors:[1,2]}
        # 新增一本图书
        book = Book.objects.create(name=validated_data.get('name'), price=validated_data.get('price'),
                                   publish_id=validated_data.get('publish'))

        # 作者也要关联上
        # book.authors   add remove set clear....
        book.authors.add(*validated_data.get('authors'))
        # book.authors.add(1,2)
        return book

修改图书接口

# 新增图书接口
	-前端传入的数据格式:{name:红楼梦,price:19,publish:1,authors:[1,2]}
###视图类
class BookDetailView(APIView):
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '修改成功'})
        else:
            return Response({'code': 101, 'msg': ser.errors})
        
        
        
 # 序列化类
#### 反序列化的多表关联的保存
class BookSerializer(serializers.Serializer):
    # name和price  既用来序列化,又用来反序列化   即写又读 ,不用加read_only,write_only
    name = serializers.CharField(max_length=8)
    price = serializers.CharField()

    # 只用来做反序列化  只写  write_only
    publish = serializers.CharField(write_only=True)
    authors = serializers.ListField(write_only=True)


    # 修改要重写update
    def update(self, instance, validated_data):
        # validated_data 校验过后的数据,{name:红楼梦,price:19,publish:1,authors:[1,2]}
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish_id = validated_data.get('publish')

        # 先清空,再add
        authors = validated_data.get('authors')
        instance.authors.clear()
        instance.authors.add(*authors)

        instance.save()

        return instance

反序列化字段校验其他

# 4层
	-1 字段自己的:举例:name = serializers.CharField(max_length=8, error_messages={'max_length': '太长了'})
    -2 validators=[方法,]   忽略掉
    -3 局部钩子
    -4 全局钩子

ModelSerializer使用

# MOdelserializer 集成自Serializer,帮咱们完成了很多操作
	-模型表强关联
     -大部分请求,不用写create和update了
        
        
### ModelSerializer的使用
class BookSerializer(serializers.ModelSerializer):
    # 跟表有关联
    class Meta:
        model = Book  # 跟book表建立了关系     序列化类和表模型类
        # fields = '__all__'  # 序列化所有Book中的字段  id name price publish authors
        fields = ['name', 'price', 'publish_detail', 'author_list', 'publish', 'authors']  # 序列化所有Book中的name和price字段字段

        # 定制name反序列化时,最长不能超过8   给字段类加属性---方式一
        extra_kwargs = {'name': {'max_length': 8},
                        'publish_detail': {'read_only': True},
                        'author_list': {'read_only': True},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        }

    # 如果Meta写了__all__ ,就相当于,复制了表模型中的所有字段,放在了这里,做了个映射
    # name = serializers.CharField(max_length=32)
    # price = serializers.CharField(max_length=32)

    # 定制name反序列化时,最长不能超过8   给字段类加属性---方式二,重写name字段
    # name = serializers.CharField(max_length=8)

    # 同理,所有的read_only和wirte_only都可以通过重写或使用extra_kwargs传入

    # 终极,把这个序列化类写成跟之前一模一样项目
    # publish_detail = serializers.SerializerMethodField(read_only=True)
    # def get_publish_detail(self, obj):
    #     return {'name': obj.publish.name, 'addr': obj.publish.addr}
    # author_list = serializers.SerializerMethodField(read_only=True)
    # def get_author_list(self, obj):
    #     l = []
    #     for author in obj.authors.all():
    #         l.append({'name': author.name, 'phone': author.phone})
    #     return l

    # 局部钩子和全局钩子跟之前完全一样
    def validate_name(self, name):
        if name.startswith('sb'):
            raise ValidationError('不能sb')

        else:
            return name
			
			
ModelSerializer:继承自Serializer
    class BookSerializer(ModelSerializer):
        # 如果字段是映射过来的,也会把字段属性(反序列化校验规则)映射过来,有可能会校验失败,不能写入
        # 可以重写这个字段,不加任何规则,取消掉它
        name = serializer.CharField()
        class Meta:
            model = Book  # 跟表进行关联
            fields = [要使用到的字段名,可以是表里面的,也可以是关联表里面的]
            extra_kwargs = {}  # 给字段类增加属性:read_only  write_only用的多
     # book表里面没有的字段,我们就要在序列化类里面再写一下
    gender = serializer.CharField()
    addr = serializer.CharField()
 
反序列化的字段,一定跟表模型的字段是对应的么,不一定

反序列化类校验部分源码解析(了解)

# 反序列化校验,什么时候,开始执行校验
	-视图类中的ser.is_valid(),就会执行校验,校验通过,返回True,不通过返回False
 
# 入口:ser.in_valid()是序列化类的对象,假设序列化类是BookSerializer---》is_valid---》找不到,找到父类BaseSerializer中有:【raise_exception:先注意】
    def is_valid(self, *, raise_exception=False):
      
        if not hasattr(self, '_validated_data'):
            try:
            # self序列化类的对象,属性中没有_validated_data,一定会走这一条,添加一个方法,所以找到这个方法
                self._validated_data = self.run_validation(self.initial_data)
            except ValidationError as exc:
                self._validated_data = {}
                self._errors = exc.detail
            else:
                self._errors = {}

        if self._errors and raise_exception:
            raise ValidationError(self.errors)

        return not bool(self._errors)
 # self._validated_data = self.run_validation(self.initial_data)核心---》self序列化类的对象
	-切记一定不要按住ctrl键点击
    -真正的执行顺序是,从下往上找,找不到,再往上
    -最终从Serializer类中找到了run_validation,而不是Field中的run_validation
    
        def run_validation(self, data=empty):
        """
        这个函数的注释:
        我们重写默认的run_validation',因为由验证器和".validate() '方法执行的验证应该使			用'non_fields_error'键强制放入错误字典。
        """
        # 字段自己的,validates方法
        (is_empty_value, data) = self.validate_empty_values(data)
        if is_empty_value:
            return data
		# 局部钩子
        value = self.to_internal_value(data)
        try:
            self.run_validators(value)
            # 全局钩子
            value = self.validate(value)
        except (ValidationError, DjangoValidationError) as exc:
            raise ValidationError(detail=as_serializer_error(exc))

        return value
    
# 局部钩子self.to_internal_value(data),self还是BookSerializer的对象,从根上找,还是在Serializer
    def to_internal_value(self, data):
        # 属性
        ret = OrderedDict()
        errors = OrderedDict()
        fields = self._writable_fields
		# fields写在序列化类中一个个字段类的对象
        for field in fields:
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            primitive_value = field.get_value(data)
            try:
                # 在执行BookSerializer类中的validate_name方法,传入了要校验的数据
                validated_value = field.run_validation(primitive_value)
                if validate_method is not None:
                    validated_value = validate_method(validated_value)
            except ValidationError as exc:
                errors[field.field_name] = exc.detail
            except DjangoValidationError as exc:
                errors[field.field_name] = get_error_detail(exc)
            except SkipField:
                pass
            else:
                set_value(ret, field.source_attrs, validated_value)

        if errors:
            raise ValidationError(errors)

        return ret
# 了解self._writable_fields,其实就是做了一个判断,字段类的属性是不是只读的,因为不是只读,代表就是只写或者写和读都有,这样的字段是反序列化使用的字段
    def _writable_fields(self):
        for field in self.fields.values():
            if not field.read_only:
                yield field

断言

# 源码中大量使用try和断言

# 关键字assert,就是断定你是xx,如果不是就抛异常

name = 'lqz'

# if name == 'lqz':
#     print('对了')
# else:
#     # print('错了')
#     raise Exception('名字不为lqz,不能继续走了')

assert name == 'lqz'  # 断定是,如果不是,就抛异常
print('后续代码')

drf之请求

Request能够解析的前端传入的编码格式

# 需求是该接口只能接受json格式,不能接受其他格式

# 方式一:在集成自APIView及其子类的视图类中配置(局部配置)
# 总共有三个:from rest_framework.parsers import JSONParser,FormParser,MultiPartParser

class BookView(APIView):
    parser_classes = [JSONParser,]
    
# 方式二:在配置文件中配置(影响所有,全局配置)
-django有套默认配置,每个项目有个配置
    -drf有套默认配置,每个项目也有个配置---》就在django的配置文件中
    REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        # 'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        # 'rest_framework.parsers.MultiPartParser',
    ],
}
    
 # 方式三:全局配了1个,某个视图类想要3个,怎么配?
	-只需要在视图类,配置3个即可
    -因为:先从视图类自身找,找不到,去项目的drf配置中找,再找不到,去drf默认的配置找

Request类有哪些属性和方法

request.data    post,put等请求放在请求体中的
__getattr__		request点不存在的时候会出发老的request的方法
query_params	老的request.GET

drf之响应

Response能够响应的编码格式

# drf 是django的一个app,所以要注册
# drf的响应,如果使用浏览器和postman访问同一个接口,返回格式是不一样的
	-drf做了判断,如果是浏览器,好看一些,如果是postman只要json数据
    
# 方式一:在视图类中写(局部配置)
	-两个响应类---》找---》def的配置文件中找--》两个类
    -from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
    class BookView(APIView):
    	renderer_classes=[JSONRenderer,]
  
# 方式二:在项目配置文件中写(全局配置)
    REST_FRAMEWORK = {
      'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ],
}
    
# 方式三:使用顺序(一般就用内置的即可)
	优先使用视图类中的配置,其次使用项目配置文件中的配置,最后使用内置的

Response的源代码属性或方法

# drf的Respnose 源码分析
from rest_framework.response import Response、
视图类的方法返回时,return Response,走他的__init__中可以传什么参数

def __init__(self, data=None,
             status=None,
             template_name=None, 
             headers=None,
             exception=False, 
             content_type=None):
	-data:就是我们之前写的ser.data,返回给前端的数据,可以是字典或列表,字符串---》序列化后返回给前端---》前端在响应体中看到的就是这个
 	-status:http响应的状态码,默认是200,可以修改
        -drf在status包下,把所有http响应状态码都写了一遍,常量
        -from rest_framework.status import HTTP_200_OK
        -Response('dddd',status=status.HTTP_200_OK)
        
    -template_name:了解即可,修改响应模板的样子,BrowsableAPIRenderer定死的样子,后期公司可以自己定制
     -headers:响应头,http响应的响应头
            -考你,原生djagno,如何像响应头中加东西
            # 四件套 render,redirect,HttpResponse,JsonResponse
            obj = HttpResponse('dddd')
            obj['xxc'] = 'yyc'
            return obj
        
   -content_type :响应编码格式,一般不动

# 重点data,status,headers

视图组件介绍及两个视图基类

# drf视图,视图类,学过的APIView,def的基类,def提供的最顶层的楼


# APIView跟之前的view区别
	-传入到视图方法中的是REST frameword的Request对象,而不是Django的HttpRequest对象
     -视图方法可以返回REST framework的Response对象
     -任何APIException异常都会被捕获到,并且处理成合适的响应信息
     -在进行dispatch()分发前,会对请求进行身份认证,权限检查,流量控制
      
# 两个视图基类
	APIView
    	类属性:
        	renderer_classes  # 响应格式
            parser_classes	  # 能够解析的请求格式
            authentication_classes  # 认证类
            thorttle_classes		#频率类
            permission_classes		# 权限类
         基于APIView+ModelSerializer+Response写5个接口

APIView+ModelSerializer+Resposne写5个接口

视图类

from .models import Book
from .serializer import BookSerializer


class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        ser = BookSerializer(instance=books, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            # 咱们现在只有ser序列化类的对象,但是咱们想要,新增的对象---》序列化成字典---》大前提,序列化类中的create方法一定要返回新增的对象
            return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
        else:
            return Response({'code': 101, 'msg': ser.errors})


class BookDetailView(APIView):
    def get(self, request, pk):
        books = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=books)
        return Response(ser.data)

    def put(self, request, pk):
        books = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=books, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
        else:
            return Response({'code': 101, 'msg': ser.errors})

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

序列化类

### ModelSerializer的使用
class BookSerializer(serializers.ModelSerializer):
    # 跟表有关联
    class Meta:
        model = Book
        fields = ['name', 'price', 'publish_detail', 'author_list', 'publish', 'authors']
        extra_kwargs = {'name': {'max_length': 8},
                        'publish_detail': {'read_only': True},
                        'author_list': {'read_only': True},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        }

路由

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

基于GenericAPIView

视图层

"""
GenericAPIView的属性:
1 queryset:要序列化或反序列化的表模型数据
2 serializer_class: 使用的序列化类
3 lookup_field		查询单条的路由分组分出来的字段名
4 filter_backends	过滤类的配置
5 pangintion_class  分页类的配置

方法:
get_queryset		获取要序列化的对象
get_object			获取单个对象
get_serializer		获取序列化类
"""
from rest_framework.generics import GenericAPIView

class BooksView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = Bookserializer

    def get(self, request):
        obj = self.get_queryset()
        ser = self.get_serializer(instance=obj,many=True)
        return Response(ser.data)

    def post(self, request):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '新增成功'})


class BookView(GenericAPIView):
    queryset = Book
    serializer_class = Bookserializer

    def get(self, request, pk):
        # 这里参数必须是pk,这样就可以查询到了lookup_field = 'pk',因为这个参数问题
        book = self.get_object()
        # 序列化
        ser = Bookserializer(instance=book)
        return Response(ser.data)

    # 修改一个
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = Bookserializer(instance=book, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '修改成功'})
        return Response({'code': 101, 'msg': '修改失败', 'result': ser.errors})

基于GenericAPIView+5个视图扩展类

from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin, \
    ListModelMixin
    
    CreateModelMixin: 创建,create方法
    UpdateModelMixin:更新,update方法
    DestroyModelMixin:删除,destroy方法
    RetrieveModelMixin:查询单条,retrive方法
    ListModelMixin:查询所有,list方法
posted @ 2023-02-01 18:20  雪语  阅读(46)  评论(0编辑  收藏  举报