Django的DRF的安装到使用到序列化组件到视图类到认证和权限组件介绍

一、drf的安装和使用

1 djangorestframework:django的app,只能再django上使用
2 pip3 install djangorestframework
3 简单使用,看代码
	django: 2.0.71版本也可以
    djangorestframework:3.12.1(默认把Django更新至最新版本)

二、restful规范

1 Representational State Transfer:表征性状态转移
2 Web API接口的设计风格,尤其适用于前后端分离的应用模式中
3 与语言,平台无关,任何框架都可以写出符合restful规范的api接口
4 规范:10-1  数据的安全保障:url链接一般都采用https协议进行传输
    -2  接口特征表现:api关键字标识
    	-https://api.baidu.com/books/
        -https://www.baidu.com/api/books/
    -3 多版本共存:url链接中标识接口版本
    	-https://api.baidu.com/v1/books/
		-https://api.baidu.com/v2/books/
    -4 数据即是资源,均使用名词(可复数)***********
    	-接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
        -一般提倡用资源的复数形式,不要使用动词
        -查询所有图书
        	-https://api.baidu.com/books/
            -https://api.baidu.com/get_all_books/ # 错误示范
            -https://api.baidu.com/delete-user    # 错误的示范
            -https://api.baidu.com/user           # 删除用户的示例
                
   -5 资源操作由请求方式决定:
		https://api.baidu.com/books       - get请求:获取所有书
        https://api.baidu.com/books/1     - get请求:获取主键为1的书
        https://api.baidu.com/books       - post请求:新增一本书书
        https://api.baidu.com/books/1     - put请求:整体修改主键为1的书
        https://api.baidu.com/books/1     - patch请求:局部修改主键为1的书
        https://api.baidu.com/books/1     - delete请求:删除主键为1的书
            
   -6 过滤,通过在url上传参的形式传递搜索条件
        https://api.example.com/v1/zoos?limit=10         :指定返回记录的数量
        https://api.example.com/v1/zoos?offset=10&limit=3:指定返回记录的开始位置
        https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
        https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
        https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
            
   -7  响应状态码
		-返回数据中带状态码
		-{
     'code':100}
   -8 返回结果中带错误信息
		-{
     'code':100,'msg':'因为xx原因失败'}
   -9 返回结果,该符合以下规范
		GET /collection:返回资源对象的列表(数组)
        GET /collection/resource:返回单个资源对象(字典)
        POST /collection:返回新生成的资源对象    (新增后的对象字典)
        PUT /collection/resource:返回完整的资源对象 (修改后的对象字典)
        PATCH /collection/resource:返回完整的资源对象 (修改后的对象字典)
        DELETE /collection/resource:返回一个空文档   ()
        
   -10 返回的数据中带链接地址
		-查询id1的图书接口,返回结果示例
    	{
     'code':100,
         'msg':'成功',
         'result':
             {
     'title':'红楼梦',
              'price':12.3,
              'publish':'https://127.0.0.1/api/v1/publish/3'
             }
        }

三、APIView源码分析

1 APIview的as_view
	-内部还是执行了View的闭包函数view
    -禁用掉了csrf
    -一切皆对象,函数也是对象  函数地址.name=wuxi
2 原生View类中过的as_view中的闭包函数view
	-本质执行了self.dispatch(request, *args, **kwargs),执行的是APIView的dispatch
3 APIView的dispatch
    def dispatch(self, request, *args, **kwargs):
        # DRF的Request类的对象,内部有request._request,是原生request
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request

        try:
            self.initial(request, *args, **kwargs)
            '''
            #认证,权限,频率
            self.perform_authentication(request)
        	self.check_permissions(request)
        	self.check_throttles(request)
            '''
            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)
		# 把视图函数(类)返回的response,又包装了一下
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

四、Request类分析

1 Request类
	-request._request:原生request
    -request.data    : post请求提交的数据(urlencoded,json,formdata)
    -request.user    :不是原生的user了
    -request.query_params :原生的request.GET,为了遵循restful规范
    -requset.FILES   :新的
    -重写了__getattr__,新的request.原来所有的属性和方法,都能直接拿到
        def __getattr__(self, attr):
            return getattr(self._request, attr)

五、序列化组件介绍

#1  作用:
    1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
    	-Book--序列化器--->字典--同过drf:Response--》json格式字符串--->传给前端
    2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
    	json格式数据---drf:Request-->字典---序列化器---》Book
    3. 反序列化,完成数据校验功能
  

六、序列化组件简单使用

# 1 序列化的使用
	-新建一个py文件,myserializer.py
	-写一个序列化类继承serializers.Serializer
    -在类中写要序列化的字段
    -在视图类中,实例化得到一个序列化类的对象,把要序列化的数据传入
    	ser = models.Book.objects.all()
    	如果是queryset对象many=True
    	ser=BookSerializer(instance=res,many=True)
        如果不是
        ser=BookSerializer(instance=res)
    -得到字典
    	ser.data就是序列化后的字典
     

1、代码实现

serializer.py
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
    # 要序列化哪个字段
    id=serializers.IntegerField()
    # id=serializers.CharField()
    title=serializers.CharField(max_length=32)
    price=serializers.DecimalField(max_digits=5, decimal_places=2)
    publish=serializers.CharField(max_length=32)
models.py
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish = models.CharField(max_length=32)
views.py
from rest_framework.views import APIView
from app01 import models
from app01.serializer import BookSerializer
class Book(APIView):
    def get(self,request,*args,**kwargs):

        res=models.Book.objects.all()
        # 借助序列化器
        # 如果是多条,就是many=True
        # 如果是单个对象,就不写
        ser=BookSerializer(instance=res,many=True)
        # 通过序列化器得到的字典
        # ser.data
        print(ser.data)
        return Response(ser.data)

urls.py
path('books/', views.Book.as_view()),

七、序列化类字段类型和字段参数

# 字段类型(记列的这几个)
	-IntegerField
    -CharField
    -DecimalField
    -DateTimeField
    -。。。跟models中大差不差
    
# 常用字段参数
	-选项参数
        max_length	最大长度
        min_lenght	最小长度
        allow_blank	是否允许为空
        trim_whitespace	是否截断空白字符
        max_value	最小值
        min_value	最大值
    
    -通用参数
    	#重点
        read_only	表明该字段仅用于序列化输出,默认False,,posman中可见修改时可以不传参数
        write_only	表明该字段仅用于反序列化输入,默认False,posman中不可见,修改时必须传参数
        
        # 掌握
        required	表明该字段在反序列化时必须输入,默认True
        default	    反序列化时使用的默认值
        allow_null	表明该字段是否允许传入None,默认False
        
        # 了解
        validators	该字段使用的验证器
        error_messages	包含错误编号与错误信息的字典
    

八、序列化器的保存功能

# 如果序列化类继承的是Serializer,必须重写create方法
# 使用方式
	-视图类
     def post(self, request):
        print(request.data)
        ser = BookSerializer(data=request.data)
        if ser.is_valid():  # 校检数据
            ser.save()  # 保存到数据库中
            return Response(ser.data)
        else:
            # 没有校验通过的错误信息
            return Response(ser.errors)
    -序列化类  
    class BookSerializer(serializers.Serializer):
        ...

        def create(self, validated_data):
            res=models.Book.objects.create(**validated_data)
            print(res)
            return res

九、序列化器的字段校验功能

# 三种方式
	1-字段自己的校验规则(max_length...2-validators的校验
        publish = serializers.CharField(max_length=32,validators=[check,])

        def check(data):
        if len(data)>10:
            raise ValidationError('最长不能超过10')
        else:
            return data
   3-局部和全局钩子
        # 局部钩子,validate_字段名,需要带一个data,data就是该字段的数据
    def validate_title(self, data):
        if data.startswith('sb'):
            raise ValidationError('不能以sb开头')
        else:
            return data
    # 全局钩子
    def validate(self, attrs):
        title=attrs.get('title')
        publish=attrs.get('publish')
        if title==publish:
            raise ValidationError('书名不能跟出版社同名')
        else:
            return attrs

十、read_only和write_only

    read_only	表明该字段仅用于序列化输出,默认False
    write_only	表明该字段仅用于反序列化输入,默认False
    
    
    class BookSerializer(serializers.Serializer):
        # 要序列化哪个字段
        id = serializers.IntegerField(required=False)
        # id=serializers.CharField()
        title = serializers.CharField(max_length=32,min_length=2,read_only=True)
        price = serializers.DecimalField(max_digits=5, decimal_places=2)
        # 序列化的时候看不到
        publish = serializers.CharField(max_length=32,validators=[check,],write_only=True)
    

十一、修改,删除接口

from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
from app01.serializer import BookSerializer


class Book(APIView):
    def put(self, request, id):
     # 通过id取到对象
        res = {
     'code': 100, 'msg': ''}
        try:
            book = models.Book.objects.get(id=id)
            ser = BookSerializer(instance=book, data=request.data)
            ser.is_valid(raise_exception=True)
            ser.save()  # 这里修改数据必须要重写BookSerializer中的update方法
            res['msg'] = '修改成功'
            res['result'] = ser.data

        except Exception as e:
            res['code'] = 101
            res['msg'] = str(e)

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



from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    title = serializers.CharField(max_length=32,min_length=2)
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    publish = serializers.CharField(max_length=32)

    def create(self, validated_data):
        res=models.Book.objects.create(**validated_data)
        print(res)
        return res

    def update(self, book, validated_data):
        book.title=validated_data.get('title')
        book.price=validated_data.get('price')
        book.publish=validated_data.get('publish')
        book.save()
        return book

十二、高级用法之source

models.py
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32,null=True)
    price = models.DecimalField(max_digits=5, decimal_places=2,null=True)
    # publish = models.CharField(max_length=32)
    publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)

    def test(self):
        # python是强类型语言,不支持字符串和数字直接相加
        return self.title+str(self.price)


class Publish(models.Model):
    name=models.CharField(max_length=32)
    addr=models.CharField(max_length=32)
    def __str__(self):
        return self.name


serializer.py
class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    name = serializers.CharField(max_length=32,min_length=2,source='title')
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    publish = serializers.CharField(max_length=32,source='publish.name')
    xxx=serializers.CharField(source='test')
    publish_addr=serializers.CharField(source='publish.addr')
    
    
1 修改返回到前端的字段名
	# source=title    字段名就不能再叫title
	name = serializers.CharField(max_length=32,min_length=2,source='title')
2 如果表模型中有方法
	# 执行表模型中的test方法,并且把返回值赋值给xxx
	xxx=serializers.CharField(source='test')
3 sourc支持跨表操作
	addr=serializers.CharField(source='publish.addr')
    
# 希望你们去看以下源码,内部如何实现的

    def bind(self, field_name, parent):
        """
        Initializes the field name and parent for the field instance.
        Called when a field is added to the parent serializer instance.
        """

        # In order to enforce a consistent style, we error if a redundant
        # 'source' argument has been used. For example:
        # my_field = serializer.CharField(source='my_field')
        assert self.source != field_name, (
            "It is redundant to specify `source='%s'` on field '%s' in "
            "serializer '%s', because it is the same as the field name. "
            "Remove the `source` keyword argument." %
            (field_name, self.__class__.__name__, parent.__class__.__name__)
        )

        # self.source should default to being the same as the field name.
        if self.source is None:
            self.source = field_name

        # self.source_attrs is a list of attributes that need to be looked up
        # when serializing the instance, or populating the validated data.
        if self.source == '*':
            self.source_attrs = []
        else:
            self.source_attrs = self.source.split('.')

十三、模型类序列化器

1 原来用的Serilizer跟表模型没有直接联系,模型类序列化器ModelSerilizer跟表模型有对应关系

2 使用
from rest_framework import serializers

class BookModelSerializer(serializers.ModelSerializer):
    # 重写某些字段
    publish = serializers.CharField(max_length=32,source='publish.name')
    class Meta:
        model=表模型    # 跟哪个表模型建立关系
        fields=[字段,字段] # 序列化的字段,反序列化的字段
        fields='__all__' # 所有字段都序列化,反序列化
        exclude=[字段,字段] # 排除哪些字段(不能跟fields同时使用)
        read_only_fields=['price','publish']  # 序列化显示的字段
        #可能有的版本不能用
        write_only_fields=['title']           # 反序列化需要传入的字段
        # 给字段额外添加参数
        extra_kwargs ={
     'title':{
     'max_length':32,'write_only':True}}
        depth=1  # 了解,跨表1查询,最多建议写3

        # 局部钩子,全局钩子,跟原来完全一样
        
        
3 新增,修改
	-统统不用重写create和update方法了,在ModelSerializer中重写了create和update
class ModelSerializer(Serializer):
    """
    * Default `.create()` and `.update()` implementations are provided.
    """

 # Default `create` and `update` behavior...
    def create(self, validated_data):
        """
        this method is essentially just:
            return ExampleModel.objects.create(**validated_data)
        """
        
        raise_errors_on_nested_writes('create', self, validated_data)

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {
     }
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        try:
            instance = ModelClass._default_manager.create(**validated_data)
 

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                field = getattr(instance, field_name)
                field.set(value)

        return instance

    
    def update(self, instance, validated_data):
        raise_errors_on_nested_writes('update', self, validated_data)
        info = model_meta.get_field_info(instance)

        # Simply set each attribute on the instance, and then save it.
        # Note that unlike `.create()` we don't need to treat many-to-many
        # relationships as being a special case. During updates we already
        # have an instance pk for the relationships to be associated with.
        m2m_fields = []
        for attr, value in validated_data.items():
            if attr in info.relations and info.relations[attr].to_many:
                m2m_fields.append((attr, value))
            else:
                setattr(instance, attr, value)

        instance.save()

        # Note that many-to-many fields are set after updating instance.
        # Setting m2m fields triggers signals which could potentially change
        # updated instance and we do not want it to collide with .update()
        for attr, value in m2m_fields:
            field = getattr(instance, attr)
            field.set(value)

        return instance

十四、高级用法之SerializerMethodField

models.py
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32,null=True)
    price = models.DecimalField(max_digits=5, decimal_places=2,null=True)
    # publish = models.CharField(max_length=32)
    publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)

class Publish(models.Model):
    name=models.CharField(max_length=32)
    addr=models.CharField(max_length=32)
    def __str__(self):
        return self.name



serializer.py
# 方式一:Serializer中
class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    name = serializers.CharField(max_length=32,min_length=2,source='title')
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    publish = serializers.SerializerMethodField()
    def get_publish(self,obj):
        dic={
     'name':obj.publish.name,'addr':obj.publish.addr}
        return dic



# 方式二:ModelSerializerr中
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'
    publish = serializers.SerializerMethodField()
    def get_publish(self,obj):
        dic={
     'name':obj.publish.name,'addr':obj.publish.addr}
        return dic
    
    
# 方式三:使用序列化类的嵌套
class PublishModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Publish
        # fields = '__all__'
        fields = ['name','addr']


class BookModelSerializer(serializers.ModelSerializer):
    publish = PublishModelSerializer()

    class Meta:
        model = models.Book
        fields = '__all__'

十五、drf的请求与相应

# Request
	-data :前端以post请求提交的数据都在它中:urlencoded\form-data\json
    -FILES :前端提交的文件
    -query_params:就是原来的request.GET
    -重写了 __getattr__
    	-使用新的request.method其实取得就是原生request.method(通过反射实现)
        
 # Response
class Response(SimpleTemplateResponse):

    def __init__(self, data=None, status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None):


	-from rest_framework.response import Response
    -对象.data:响应的字典
    -status:http响应的状态码
    	-drf提供给你了所有的状态码,以及它的意思
        from rest_framework.status import HTTP_201_CREATED
    -template_name:模板名字(一般不动),了解
    -headers:响应头,字典
    -content_type:响应的编码方式,了解        
        
        
 # 自己封装一个Response对象
      class CommonResponse:
        def __init__(self):
            self.code=100
            self.msg=''
        @property
        def get_dic(self):
            return self.__dict__
# 自己封装一个response,继承drf的Response



# 通过配置,选择默认模板的显示形式(浏览器方式,json方式)
	-配置文件方式(全局)
        -如果没有配置,默认有浏览器和json
            -drf有默认配置文件
            from rest_framework.settings import DEFAULTS
            REST_FRAMEWORK = {
     
            'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
                'rest_framework.renderers.JSONRenderer',  # json渲染器
                'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器
            )
        	}
    -在视图类中配置(局部)
    	-粒度更小
        -class BookDetail(APIView):
    		renderer_classes = [JSONRenderer,]
        
    

十六、many=True源码分析,局部全局钩子源码解析

1 many=True
	-__init__----->一路找到了BaseSerializer---》__new__决定了生成的对象是谁
    
class BaseSerializer(Field):
    
  def __new__(cls, *args, **kwargs):
        # We override this method in order to automatically create
        # `ListSerializer` classes instead when `many=True` is set.
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)
        return super().__new__(cls, *args, **kwargs)
    
   @classmethod
    def many_init(cls, *args, **kwargs):
        allow_empty = kwargs.pop('allow_empty', None)
        child_serializer = cls(*args, **kwargs)
        list_kwargs = {
     
            'child': child_serializer,
        }
        if allow_empty is not None:
            list_kwargs['allow_empty'] = allow_empty
        list_kwargs.update({
     
            key: value for key, value in kwargs.items()
            if key in LIST_SERIALIZER_KWARGS
        })
        meta = getattr(cls, 'Meta', None)
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
        return list_serializer_class(*args, **list_kwargs)  
    
    
    
2 入口是is_valid()---》BaseSerializer--》is_valid---》self._validated_data = self.run_validation(self.initial_data)
	-Serializer这个类的:self.run_validation
def run_validation(self, data=empty):
        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

十七、2个视图基类

# Django REST framwork 提供的视图的主要作用:
    控制序列化器的执行(检验、保存、转换数据)
    控制数据库查询的执行
# APIView:继承了原生Django的View
1、models..py
from django.db import models


class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    publish = models.CharField(max_length=32)

    
2、bookserializer.py
from rest_framework import serializers
from app01 import models


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


3、views.py
from django.shortcuts import render
from app01 import models, bookser
from rest_framework.views import APIView
from rest_framework.response import Response


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

    def post(self, request):
        ser = bookser.BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)


class BookDetailSerializer(APIView):
    def get(self, request, pk):
        book = models.Book.objects.filter(pk=pk).first()
        ser = bookser.BookSerializer(instance=book)
        return Response(ser.data)

    def put(self, request, pk):
        book = models.Book.objects.filter(pk=pk).first()
        ser = bookser.BookSerializer(instance=book, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

    def delete(self, request, pk):
        models.Book.objects.filter(pk=pk).delete()
        return Response('删除成功')


4、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.BookSerializer.as_view()),
    re_path('books/(?P\d+)', views.BookDetailSerializer.as_view()),
]

5、settings.py

INSTALLED_APPS = [
    'rest_framework'
]


# GenericAPIView:继承了APIView
	-queryset = models.Book.objects.all()
    -serializer_class = serializer.BookModelSerializer
    -get_queryset:获取配置的queryset
    -get_object:路由中的分组字段必须是pk
    -get_serializer:获取配置的序列化类
    
    
1、bookserizlizer.py

from app01 import models, bookser
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
    
    
class BookGenericAPIView(GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = bookser.BookSerializer

    def get(self, request, *args, **kwargs):
        book_list = self.get_queryset()
        ser = self.serializer_class(instance=book_list, many=True)
        return Response(ser.data)

    def post(self, request, *args, **kwargs):
        ser = self.serializer_class(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)


class BookDetailGenericAPIView(GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = bookser.BookSerializer

    def get(self, request, *args, **kwargs):
        book = self.get_object()
        ser = self.serializer_class(instance=book)
        return Response(ser.data)

    def put(self, request, *args, **kwargs):
        book = self.get_object()
        ser = self.serializer_class(instance=book, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

    def delete(self, request, *args, **kwargs):
        models.Book.objects.filter(pk=kwargs.get('pk')).delete()
        return Response('删除成功')

2、urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookGenericAPIView.as_view()),
    re_path('books/(?P\d+)', views.BookDetailGenericAPIView.as_view()),
]

    
 # 最终总结
#两个基类
APIView:如果跟models没有关系(没有数据库相关操作),就继承它
GenericAPIView:有关数据库操作,queryset 和serializer_class

十八、5个视图扩展类

#5个视图扩展类(rest_framework.mixins)
CreateModelMixin:                     create方法创建一条
DestroyModelMixin:destory方法删除一条
ListModelMixin:list方法获取所有
RetrieveModelMixin:retrieve获取一条
UpdateModelMixin:update修改一条

1、bookserializer.py
from app01 import models, bookser
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, UpdateModelMixin



class BookMixin(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = bookser.BookSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class BookDetailMixin(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = bookser.BookSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)
    
    
2、urls.py
urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookMixin.as_view()),
    re_path('books/(?P\d+)', views.BookDetailMixin.as_view()),
]

十九、9个子类视图

|| 9个子类视图(rest_framework.generics) | 继承 | 方法 |
| :----------------------------------- | ------------------------------------------------------------ | ----------------------------------------- |
| CreateAPIView | CreateModelMixin,GenericAPIView | 有post方法,新增数据 |
| DestroyAPIView | CreateModelMixin,GenericAPIView | 有delete方法,删除数据 |
| ListAPIView | ListModelMixin,GenericAPIView | 有get方法获取所有 |
| UpdateAPIView | UpdateModelMixin,GenericAPIView | 有put和patch方法,修改单个数据 |
| RetrieveAPIView | RetrieveModelMixin,GenericAPIView | 有get方法,获取一条 |
| ListCreateAPIView | ListModelMixin,CreateModelMixin,GenericAPIView | 有get获取所有,post方法新增 |
| RetrieveDestroyAPIView | RetrieveModelMixin,DestroyModelMixin,GenericAPIView | 有get方法获取一条,delete方法删除 |
| RetrieveUpdateAPIView | RetrieveModelMixin,UpdateModelMixin,GenericAPIView | 有get获取一条,put,patch修改 |
| RetrieveUpdateDestroyAPIView | RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView | 有get获取一条,put,patch修改,delete删除 || |
|–|--|
| | |

1、bookserializer.py

from app01 import models, bookser
from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView, ListCreateAPIView, RetrieveUpdateAPIView, RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView

class BookListCreateAPIView(ListCreateAPIView):
    queryset = models.Book.objects.all()
    serializer_class = bookser.BookSerializer


class BookDetaiRetrieveUpdateDestroyAPIView(RetrieveUpdateDestroyAPIView):
    queryset = models.Book.objects.all()
    serializer_class = bookser.BookSerializer


2、urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookListCreateAPIView .as_view()),
    re_path('books/(?P\d+)', views.BookDetaiRetrieveUpdateDestroyAPIView.as_view()),
]

二十、视图集

1、ViewSet

ViewSetMixin
APIView

1、ser.py
from rest_framework.viewsets import  ViewSet


class WuView(ViewSet):
    def wuxi(self, request,*args,**kwargs):
        return Response('wuxi')

2、urls.py
urlpatterns = [
    path('admin/', admin.site.urls),
    path('wuxis/', views.WuView .as_view({
     'get' 'wuxi'})),
]

2、GenericViewSet

ViewSetMixin, 
generics.GenericAPIView

3、ModelViewSet

mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet

1、bookserializer.py

from app01 import models, bookser
from rest_framework.viewsets import ModelViewSet


class BookListModelViewSet(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = bookser.BookSerializer


2、urls.py

urlpatterns = [
    path('books/', views.BookListModelViewSet.as_view({
     'get': 'list', 'post': 'create'})),
    re_path('books/(?P\d+)', views.BookListModelViewSet.as_view({
     'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]

4、ReadOnlyModelViewSet(只能查询数据)

mixins.RetrieveModelMixin,
mixins.ListModelMixin,GenericViewSet

1、bookserializer.py
from rest_framework.viewsets import ReadOnlyModelViewSet

class BookListReadOnly(ReadOnlyModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = bookser.BookSerializer


2.urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookListReadOnly.as_view({
     'get': 'list'})),
    re_path('books/(?P\d+)', views.BookListReadOnly.as_view({
     'get': 'retrieve'})),
]


1、ViewSet的映射关系

![1604654204669](1604654204669.png)

2、ReadOnlyModelViewSet只能查询,不能新增、修改、删除

![1604655009360](1604655009360.png)

## 5、action的使用

```python
# 只要继承了ViewSetMixin类
# 路由配置:path('books_mix/', views.BookView.as_view({'get':'lqz'}))
# 视图类的方法中就会有个action
class BookView(ViewSet):
    def lqz(self,request,*args,**kwargs):
        print(self.action)
        return Response('lqz')
    
    
# ViewSetMixin以后只要继承它,路由的配置就发生变化了,只需要写映射即可

二十一、路由的使用

# 自动生成路由
# SimpleRouter
# DefaultRouter

# 继承了ViewSetMixin的视图类,以后写路由,可以自动生成
from rest_framework import routers
# 实例化得到一个对象
router = routers.SimpleRouter()
# 注册进路由
router.register('books', views.BookSetView)
# 把自动生成的路由配置到urlpatterns中
	-urlpatterns += router.urls
    -re_path(r'v1/', include(router.urls))
    
    
1.urls.py

from rest_framework import routers

router = routers.SimpleRouter()
router.register('books', views.BookModelViewSet, basename='book')   
urlpatterns = [
    path('admin/', admin.site.urls),
]
urlpatterns += router.urls

print(router.urls)


2、bookserializer.py
from rest_framework.viewsets import ReadOnlyModelViewSet

class BookListReadOnly(ReadOnlyModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = bookser.BookSerializer

=========================================================================================

[<URLPattern '^books/$' [name='book-list']>,
 <URLPattern '^books/(?P[^/.]+)/$' [name='book-detail']>]    
    
    
# 配置路由的方式
	-最原始的
    	-path('books/', views.BookAPIView.as_view()),
    -ViewSetMixin的视图类
    	-path('books_set/', views.BookSetView.as_view({
     'get':'list','post':'create'}))
    -ViewSetMixin的视图类
    	-自动生成,上面讲的
        
        
        
        
 # action
	-当自动生成路由的时候,由于视图类中还有其它方法,是无法自动生成路由的
    -加action装饰器:
    	-methods:什么请求方式会触发被装饰函数的执行
        -detail:True是基于带id的路由生成的,如果是False,是基于不带id的路由生成的
    	-@action(methods=['get'], detail=True)

1、bookserializer.py
from rest_framework.decorators import action

class BookModelViewSet(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = bookser.BookSerializer

    @action(methods=['get'], detail=False)  # 基于不带id的路由生成
    def login(self, request):
        return Response('login')

2、urls.py
from rest_framework import routers

router = routers.SimpleRouter()
router.register('books', views.BookModelViewSet, basename='book')
urlpatterns = [
    path('admin/', admin.site.urls),
]
urlpatterns += router.urls

print(router.urls)

[<URLPattern '^books/$' [name='book-list']>, 
 <URLPattern '^books/login/$' [name='book-login']>,  # 基于不带id的路由生成
 <URLPattern '^books/(?P[^/.]+)/$' [name='book-detail']>]




3、bookserializer.py
from rest_framework.decorators import action

class BookModelViewSet(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = bookser.BookSerializer

    @action(methods=['get'], detail=True)  # 基于带id的路由生成
    def login(self, request, pk):  # 注意这里需要参数
        return Response('login')

4、urls.py
from rest_framework import routers

router = routers.SimpleRouter()
router.register('books', views.BookModelViewSet, basename='book')
urlpatterns = [
    path('admin/', admin.site.urls),
]
urlpatterns += router.urls

print(router.urls)

[<URLPattern '^books/$' [name='book-list']>, 
 <URLPattern '^books/(?P[^/.]+)/$' [name='book-detail']>, 
 <URLPattern '^books/(?P[^/.]+)/login/$' [name='book-login']>]  # 基于带id的路由生成

二十二、认证功能源码分析

1 APIView---》dispatch---》

self.initial(request, *args, **kwargs)--》self.perform_authentication(request)

---》Request.user--->self._authenticate(self):Request类的方法
    
    ---》self.authenticators:Request类的属性---》在Request对象实例化的时候传入的----》
        
        Request在什么时候实例化的?dispatch的时候---》APIView:self.get_authenticators()--return [auth() for auth in self.authentication_classes]----》
    
    如果在自己定义的视图类中写了authentication_classes=[1,类2]----》
    
    Request的self.authenticators就变成了我们配置的一个个类的对象
        
        
2 self._authenticate(self):Request类的方法
def _authenticate(self):
     for authenticator in self.authenticators: # BookView中配置的一个个类的对象
            try:
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return
3 只要在视图类中配置authentication_classes = [MyAuthen.LoginAuth, ]
	就会执行上面的方法,执行认证

二十三、自定义认证类

1 使用
	-定义一个类,继承BaseAuthentication
    
    
models.py
class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    user_type = models.IntegerField(choices=((1, '超级用户'), (2, '普通用户'), (3, '二笔用户')),default=1)


class UserToken(models.Model):
    user = models.OneToOneField(to='User',on_delete=models.CASCADE)
    token = models.CharField(max_length=64)
    

AppAuthentication.py    
from app01 import models
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication    
    
class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 写认证规则,如果过认证通过,就正常return
        # 如果认证失败,抛异常
        # 取出携带的随机字符串,去数据判断,如果有正常登录,没有,告诉它没有登录
        # 在get请求的参数中,放到请求头中
        token = request.GET.get('token')

        res = models.UserToken.objects.filter(token=token).first()
        if res:
            return (res.user, token)
        else:
            raise AuthenticationFailed('您没有登录')
            
Views.py            
class LoginView(APIView):
    authentication_classes = []  # 局部禁用
    def post(self, request):
        res = {
     'code': 100, 'msg': '登录成功'}
        username = request.data.get('username')
        password = request.data.get('password')
        # 查询数据库
        user = models.User.objects.filter(username=username, password=password).first()

        if user:
            token = uuid.uuid4()  # 生成一个随机字符串
            # 把token存到UserToken表中(如果是第一次登录:新增,如果不是第一次:更新)
            models.UserToken.objects.update_or_create(defaults={
     'token': token}, user=user)
            res['token'] = token
            return Response(res)
        else:
            res['code'] = 101
            res['msg'] = '用户名或密码错误'

            return Response(res)


    -重写authenticate方法
    -局部使用和全局使用
        -局部:在视图类中配置(只要配置了,就是登录以后才能访问,没配置,不用登录就能访问)
            authentication_classes = [MyAuthen.LoginAuth, ]
        -全局
        REST_FRAMEWORK = {
     
        "DEFAULT_AUTHENTICATION_CLASSES": ["app01.MyAuthen.LoginAuth", ]
        }
        
   -注意:
	1 认证类,认证通过可以返回一个元组,有两个值,第一个值会给,request.user,第二个值会个request.auth
    2 认证类可以配置多个,按照从前向后的顺序执行,如果前面有返回值,认证就不再继续往下走了

二十四、自定义权限功能

1 登录成功以后,超级用户可以干某些事,普通用户不能干---》超级用户可以查看某些接口,普通用户不能查看


2 使用写一个类继承BasePermission,重写has_permission
    class SuperPermission(BasePermission):
        def has_permission(self, request, view):
            # Return `True` if permission is granted, `False` otherwise.
            # 超级用户可以访问,除了超级用户以外,都不能访问
            if request.user.user_type == '1':
                return True
            else:
                return False
            
3 局部使用和全局使用
	-在想局部使用的视图类上
	permission_classes = [MyAuthen.SuperPermission]
    -全局使用
      REST_FRAMEWORK = {
     
        "DEFAULT_PERMISSION_CLASSES": ["app01.MyAuthen.SuperPermission", ]
        }
     -局部禁用
    permission_classes = []

二十五、权限源码

 APIView---》dispatch---》self.initial(request, *args, **kwargs)---》self.check_permissions(request)

---》self.get_permissions()

 def check_permissions(self, request):
        """
        Check if the request should be permitted.
        Raises an appropriate exception if the request is not permitted.
        """
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request,
                    message=getattr(permission, 'message', None),
                    code=getattr(permission, 'code', None)
                ) def check_permissions(self, request):
        

def get_permissions(self):
    """
    Instantiates and returns the list of permissions that this view requires.
    """
    return [permission() for permission in self.permission_classes]



 message = {
     "detail": '您的权限不够'}

你可能感兴趣的:(Django,django)