首页 > 其他分享 >drf

drf

时间:2023-05-31 11:14:00浏览次数:24  
标签:return get self request data def drf

drf

1 web应用模式

1 混合开发
2 前后端分离

api接口

#通过网络,规定前后台信息的交互规则的url链接,也就是前后的交互的媒介

restful规范

'''
-1 数据的安全保障,通常使用https(http+ssl/tsl)协议
        -url链接一般都采用https协议进行传输
        -采用https协议,可以提高数据交互过程中的安全性
        
   -2 接口中带api标识
		-https://api.lqz.com/books
        -https://www.lqz.com/api/books    咱们用这个
            
   -3 多版本共存,路径中带版本信息
		-https://api.lqz.com/v1/login
		-https://www.lqz.com/api/v2/login
            
   -4 数据即是资源,均使用名词,尽量不出现动词(最核心的)
		-接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
    	-接口形式如下
            https://api.baidu.com/users
            https://api.baidu.com/books
       -特殊的接口可以出现动词,因为这些接口一般没有一个明确的资源,或是动词就是接口的核心含义、				https://api.baidu.com/login
        
        
   -5 资源操作由请求方式决定(method)
		-操作资源一般都会涉及到增删改查,我们提供请求方式来标识增删改查动作
        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  - delete请求:删除主键为1的书
            
            
   -6 在请求地址中带过滤条件
		https://api.baidu.com/books?name=红&price=99
   -7 响应中状态码:两套
		-http响应状态码:1xx:请求正在处理,2xx:成功响应,3xx:重定向,4xx:客户端错误,5xx:服务端错误
    		-https://blog.csdn.net/li_chunlong/article/details/120787872
        -公司内部规定的响应状态码,放在响应体中
        	{code:0}   咱们后期一般使用100  101 102这种
    -8 返回数据中带错误信息
    	{
            code:0
    		msg: "ok/用户名错误"
		}
        
    -9 返回的结果应该符合以下规范---》好多公司不遵循这个
        GET 获取所有数据:返回资源对象的列表(数组)[{name:红楼梦,price:99},{name:红楼梦,price:99},{name:红楼梦,price:99}]
        GET 单个对象:返回单个资源对象:{name:红楼梦,price:99}
        POST 新增对象:返回新生成的资源对象:{name:西游记,price:99}
        PUT 修改对象:返回完整的资源对象 :{name:西游记,price:100}
        DELETE 删除:返回一个空文档 
        
        
    -10 响应数据中带连接
'''

总结:为了数据安全,一般使用https协议,在url链接中一般带api标识和版本信息来作为区分,
而且url尽量使用名词可以携带参数,不同的请求方式,代表不同的请求内容,在返回数据中,一般带状态码和错误信息,也可以带链接,返回的数据需要安照一定的格式。

序列化和反序列化

序列化:把我们能够识别的数据类型转换为固定的格式提供给别人
反序列化:把别人提供的数据类型转换为我们能够识别的数据类型

接口总结

1 如果是get请求,获取数据,封装为列表套字典,如果带有请求参数,返回一个对象,即一个字典
2 如果是post请求,提交数据,提供序列化,保存到数据库,返回当前保存数据的对象,即一个字典
3 如果是put,修改一条数据,需要得到数据和修改的条件,返回修改后的对象,即一个字典
4 如果是delete,删除一个数据,可以返回一个空文档,也可以返回一个列表套字典

cbv源码分析

'''
当请求来了,路由匹配成功,会执行as_view()(request)===>返回view的内存地址===》执行view(request)==>返回self.dispatch ===>执行通过__getattr__得到当前请求方法的内存地址===》然后通过当前请求方式去执行类中的对应方法
'''
    -序列化类:serializer
# 6 cbv源码分析
	-路由中:path('api/v1/books/', views.BookView.as_view())---》路由匹配成功就会执行---》views.BookView.as_view()(request)---->看一下BookView.as_view是怎么写的---》View这个类中的as_view类的绑定方法---》执行结果是view的内存地址[as_view中的内存函数,闭包函数]---》本质请求来了,执行view(request)--->return self.dispatch(request, *args, **kwargs)--->对象,谁的对象,View的对象,咱们从BookView---》View---》self是BookView的对象----》发现BookView没有dispatch---》View类的dispatch
    def dispatch(self, request, *args, **kwargs):
        if request.method.lower() in self.http_method_names:
            # self是BookView的对象,通过字符串get找方法或属性,找到了get方法
            #handler就是BookView类的get方法
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        # 执行 BookView的get(request)---->get请求就会执行BookView的get方法,post请求就会执行BookView的post方法
        return handler(request, *args, **kwargs)

APIView的执行流程

'''
当请求来了,匹配成功当前url,会执行当前对应的APIView中的as_view()==>去父类中调用as_view,封装request,执行三大认证,去除了csrf认证,==》执行csrf_exempt,返回view的 内衬地址,==》执行view==>得到self.dispatch==》self是视图类的对象,得到当前请求方式的内存地址,全局异常捕获然后执行当前得到的内存地址



'''
# 总结:APIView的执行流程
	1 去除了所有的csrf
    2 包装了新的request,以后在视图类中用的request是新的request  Request类的对象,不是原生的了
    	-原生的在:新的requets._request
    3 在执行视图类的方法之前,执行了3大认证
    4 如果在3大认证或视图函数方法执行过程中出了错,会有异常捕获----》全局异常捕获
    5 以后视图类方法中的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.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 完成接口的编写

'''
序列化器使用
1 先实现一个序列化类,对应模型字段
2 通过增加字段的规范来得到对内容的规定
3 通过局部钩子,和全局钩子再一次对内容进行规范
4 因为使用序列化类来实现get,post,put,delete,需要重写create和update方法,来增加和修改数据
5 如果是get方法,需要查询到这个内容的对象,在进行序列化
6 如果是post方法,需要得到当前需要增加的内容,校验数据,保存数据,需要重写create方法,
7 如果是put方法,需要得到当前当前需要修改的对象,和修改的数据内容,实例化这个序列化器对象,验证参数,实现save方法,当需要重写update方法


'''


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

常用字段类和参数

常用字段类

字段 字段构造方式
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLField URLField(max_length=200, min_length=None, allow_blank=False)
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"
IPAddressField IPAddressField(protocol=’both’, unpack_ipv4=False, **options)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField()
ChoiceField ChoiceField(choices) choices与Django的用法相同
MultipleChoiceField MultipleChoiceField(choices)
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
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=)
# IntegerField      CharField   DateTimeField  DecimalField

# ListField和DictField---》比较重要,但是后面以案例形式讲

字段参数

选项参数(CharField,IntegerFild)

参数名称 作用
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
# read_only   write_only   很重要,后面以案例讲

反序列化校验

# 反序列化,有三层校验
    -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
            
  # 只有三层都通过,在视图类中:
	ser.is_valid():  才是True,才能保存

反序列化之保存

# 新增接口:
	-序列化类的对象,实例化的时候:ser = BookSerializer(data=request.data)
	-数据校验过后----》调用  序列化类.save()--->但是要在序列化类中重写  create方法
        def create(self, validated_data):
            book=Book.objects.create(**validated_data)
            return book
        
        
# 修改接口
	-序列化类的对象,实例化的时候:ser = BookSerializer(instance=book,data=request.data)
	-数据校验过后----》调用  序列化类.save()--->但是要在序列化类中重写  update方法
        def update(self, book, validated_data):
            for item in validated_data:  # {"name":"jinping","price":55}
                setattr(book, item, validated_data[item])
            book.save()
            return book
        
        
# 研究了一个问题
	在视图类中,无论是保存还是修改,都是调用序列化类.save(),底层实现是根据instance做一个判断

ApiView+序列化类案例

'''
class Bookserializers(serializers.Serializer):
    print(123)
    name = serializers.CharField()
    price = serializers.CharField()
    publish_id = serializers.CharField(write_only=True)
    Authors = serializers.ListField(write_only=True)
    Authors_list = serializers.ListField(read_only=True)
    publish_dict = serializers.DictField(read_only=True)

    # 1 字段自己的校验
    # 2 局部钩子
    # def validate_字段名(self, 字段名):
    #
    # # 3 局部钩子
    # def validate(self, attr):

    def update(self, instance, validated_data):
        print(11)
        Authors = validated_data.pop("Authors")
        print(Authors)
        for i in validated_data:
            setattr(instance, i, validated_data[i])
        instance.Authors.set(Authors)
        instance.save()
        return instance

    def create(self, validated_data):
        print(1)
        Author = validated_data.pop("Authors")
        book = Book.objects.create(**validated_data)
        book.Authors.add(*Author)
        book.save()
        return book


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

    def post(self, request):
        ser = Bookserializers(data=request.data)
        print(ser)
        if ser.is_valid():
            ser.save()
            return Response({"data": ser.data})
        return Response({"code": 1001, "msg": "增加失败"})


class BookDealieView(APIView):
    def get(self, request, *args, **kwargs):
        book = Book.objects.filter(pk=kwargs.get("pk")).first()
        ser = Bookserializers(instance=book, many=False)
        return Response(ser.data)

    def put(self, request, *args, **kwargs):
        book = Book.objects.filter(pk=kwargs.get("pk")).first()
        ser = Bookserializers(data=request.data, instance=book)
        if ser.is_valid():
            print(11)
            ser.save()
            return Response({"data": ser.data})
        return Response({"code": 1001, "msg": "增加失败"})

    def delete(self, request, *args, **kwargs):
        Book.objects.filter(pk=kwargs.get("pk")).delete()
        return Response({"code": 200, "msg": "删除成功"})
        '''

# 通过装饰器做,装饰器视图函数的,以后都会有request
def wrapper(func):
    def inner(request, *args, **kwargs):
        # 造个新的requeset
        # 如果是 urlencoded,form-data ----》request.POST 就有值
        # 如果request.POST 就有值没有值,就是json格式编码
        try:
            print(request.body)
            request.data = json.loads(request.body)  # 表示是json格式编码
        except Exception as e:
            request.data = request.POST

        res = func(request, *args, **kwargs)
        return res

    return inner

常用字段类

#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 BooleanField
ListField
DictField

常用字段参数

选项参数:

# CharField及其子类的(EmailField) ---》反序列化的校验,字段自己的规则
    max_length	最大长度
    min_lenght	最小长度
    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
write_only	表明该字段仅用于反序列化输入,默认False


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

序列化高级用法之source

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

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

SerializerMethodField定制

'''自定义一个字段名,字段类是SerializerMethodField,如果这个写在序列化类中,需要使用get_字段名,需要传入一个obj对象'''

# 定制关联字段的显示形式
	-一对多的,显示字典
    -多对多,显示列表套字典
    
    
    
# 代码
class BookSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=8)
    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):
        l = []
        for author in obj.authors.all():
            l.append({'name': author.name, 'phone': author.phone})
        return l

在表模型中定制

'''
1 在序列化类中写好字段名,这个字段类是当前数据展示的形式,如ListField,DictField,author_list = serializers.ListField()
2 在模型类中写这两个方法,如def author_list(self):


'''


#### 表模型
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')

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

    def author_list(self):
        l = []
        for author in self.authors.all():
            l.append({'name': author.name, 'phone': author.phone})
        return l
    
    
 # 序列化类
class BookSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=8)
    price = serializers.CharField()

    # publish_detail = serializers.CharField()
    publish_detail = serializers.DictField()
    author_list = serializers.ListField()

多表关联反序列化保存

# 视图类
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
    
    
    
    ###视图类
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

    
    
	

ModelSerializer使用

# ModelSerializer 继承自Serializer,帮咱们完成了很多操作
	-跟表模型强关联
	-大部分请求,不用写create和update了
    
# 如何使用
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},
                        }
### 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

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

# 反序列化校验,什么时候,开始执行校验
	-视图类中的 ser.is_valid(),就会执行校验,校验通过返回True,不通过返回False
    
    
# 入口:ser.is_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):
        # 字段自己的,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)
            # 全局钩子--》如果在BookSerializer中写了validate,优先走它,非常简单
            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的对象,从根上找
     def to_internal_value(self, data):
        ret = OrderedDict()
        errors = OrderedDict()
        fields = self._writable_fields
        # fields写在序列化类中一个个字段类的对象
        for field in fields:
            # self BookSerializer的对象,反射validate_name
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            try:
                # 在执行BookSerializer类中的validate_name方法,传入了要校验的数据
               validated_value = validate_method(validated_value)
            except ValidationError as exc:
                errors[field.field_name] = exc.detail
          
            else:
                set_value(ret, field.source_attrs, validated_value)

        if errors:
            raise ValidationError(errors)

        return ret
    
    -ser.is_valid---》走局部钩子的代码---》是通过反射获取BookSerializer中写的局部钩子函数,如果写了,就会执行----》走全局钩子代码---》self.validate(value)--->只要序列化类中写了,优先走自己的
    
    
   
    

断言

关键字assert

drf之请求

3.1 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 类是 Python 的 urllib 库中的一个类,用于生成 HTTP 请求。它有以下属性和方法:

属性:

full_url:Request 对象请求的完整 URL。
data:请求主体内容,可以是字节或字符串类型。
headers:一个字典,表示请求头信息。
method:HTTP 请求方法,例如 "GET" 或 "POST" 等。
url:请求的地址。
origin_req_host:原始请求地址中的主机名。
host:请求地址中的主机名。
type:URL 的协议类型,例如 "http" 或 "https" 等。
unverifiable:如果响应是不可验证的,则为 True。
方法:

add_header(header, value):在请求头中添加新的键值对。
get_header(header_name, default=None):获取请求头中指定键的值,默认值为空。
has_header(header_name):检查给定请求头是否存在。
get_method():返回请求的 HTTP 方法。
is_unverifiable():如果响应是不可验证的,则返回 True。
set_proxy(url, type):设置代理服务器的地址和类型。

drf之响应

4.1 Response能够响应的编码格式

# drf  是djagno的一个app,所以要注册
# drf的响应,如果使用浏览器和postman访问同一个接口,返回格式是不一样的
	-drf做了个判断,如果是浏览器,好看一些,如果是postman只要json数据
    
    
# 方式一:在视图类中写(局部配置)
	-两个响应类---》找---》drf的配置文件中找--》两个类
    -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',
    ],
}
    
# 方式三:使用顺序(一般就用内置的即可)
	优先使用视图类中的配置,其次使用项目配置文件中的配置,最后使用内置的

Resposne的源码属性或方法

# drf 的Response 源码分析
	-from rest_framework.response import Response
    -视图类的方法返回时,retrun Response ,走它的__init__,init中可以传什么参数
    
    
# 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,drf的基类,drf提供的最顶层的类-
# GenericAPIView:GenericAPIView继承了APIView

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

APIView+ModelSerializer+Resposne写5个接口

class Bookserializers(serializers.ModelSerializer):
    # name = serializers.CharField()
    # price = serializers.CharField()
    # publish_id = serializers.CharField(write_only=True)
    # Authors = serializers.ListField(write_only=True)
    Authors_list = serializers.ListField(read_only=True)
    publish_dict = serializers.DictField(rea                                         d_only=True)

    class Meta:
        model = Book
        # feilds = "__all__"
        fields = ['name', 'price', 'publish', 'Authors', 'Authors_list', 'publish_dict']
        extra_kwargs = {

        }

    # 1 字段自己的校验
    # 2 局部钩子
    # def validate_字段名(self, 字段名):
    #
    # # 3 局部钩子
    # def validate(self, attr):

    # def update(self, instance, validated_data):
    #     print(11)
    #     Authors = validated_data.pop("Authors")
    #     print(Authors)
    #     for i in validated_data:
    #         setattr(instance, i, validated_data[i])
    #     instance.Authors.set(Authors)
    #     instance.save()
    #     return instance
    #
    # def create(self, validated_data):
    #     print(1)
    #     Author = validated_data.pop("Authors")
    #     book = Book.objects.create(**validated_data)
    #     book.Authors.add(*Author)
    #     book.save()
    #     return book


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

    def post(self, request):
        ser = Bookserializers(data=request.data)
        print(ser)
        if ser.is_valid():
            ser.save()
            return Response({"data": ser.data})
        return Response({"code": 1001, "msg": "增加失败"})


class BookDealieView(APIView):
    def get(self, request, *args, **kwargs):
        book = Book.objects.filter(pk=kwargs.get("pk")).first()
        ser = Bookserializers(instance=book, many=False)
        return Response(ser.data)

    def put(self, request, *args, **kwargs):
        book = Book.objects.filter(pk=kwargs.get("pk")).first()
        ser = Bookserializers(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()
            return Response({"data": ser.data})
        return Response({"code": 1001, "msg": "增加失败"})

    def delete(self, request, *args, **kwargs):
        Book.objects.filter(pk=kwargs.get("pk")).delete()
        return Response({"code": 200, "msg": "删除成功"})

基于GenericAPIView+5个视图扩展类

from rest_framework.generics import GenericAPIView
from rest_framework.generics import ListAPIView, CreateAPIView, UpdateAPIView, RetrieveAPIView, DestroyAPIView
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView, RetrieveUpdateAPIView, \
    RetrieveDestroyAPIView


# 基于GenericAPIView+5个视图扩展类
# 5个视图扩展类CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin, ListModelMixin
class BookView(ListAPIView, CreateAPIView):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)


# class BookView(ListCreateAPIView):
#     queryset = Book.objects.all()
#     serializer_class = Bookserializers
#
#     def get(self, request):
#         return self.list(request)
#
#     def post(self, request):
#         return self.create(request)


class BookDealieView(RetrieveAPIView, UpdateAPIView, DestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

    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)

模块与包的使用

# 模块与包
	-模块:一个py文件,被别的py文件导入使用,这个py文件称之为模块,运行的这个py文件称之为脚本文件
    -包:一个文件夹下有__init__.py 
    
    
# 模块与包的导入问题
	
    
    

'''
0 导入模块有相对导入和绝对导入,绝对的路径是从环境变量开始的
1 导入任何模块,如果使用绝对导入,都是从环境变量开始导入起
2 脚本文件执行的路径,会自动加入环境变量
3 相对导入的话,是从当前py文件开始计算的
4 以脚本运行的文件,不能使用相对导入,只能用绝对导入
'''

视图之两个视图基类

# 视图类:
	-APIView:之前用过
	-GenericAPIView:GenericAPIView继承了APIView
    
    
    
# GenericAPIView
	-类属性:
    	queryset:要序列化的所有数据
    	serializer_class:序列化类
        lookup_field = 'pk' :查询单条时的key值
     -方法:
    	-get_queryset():获取所有要序列化的数据【后期可以重写】
        -get_serializer  : 返回序列化类
        -get_object :获取单个对象
        
        
        
 #总结:以后继承GenericAPIView写接口
	1 必须配置类属性
    	queryset
        serializer_class
     2 想获取要序列化的所有数据
    	get_queryset()
     3 想使用序列化类:
    	get_serializer
     4 想拿单条
    	get_object
    	

drf之视图--两个视图基类

# 6 drf之视图--两个视图基类
	-APIView:drf最顶层的视图类,继承了原生djagno的View
    	-1 去除了csrf
        -2 包装了新的request
        -3 在执行视图类的方法之前,执行了三大认证
        -4 处理了全局异常
        
        
    -GenericAPIView:继承了APIView
    	-类属性:
        	-queryset:要序列化的数据
            -serializer_class:指定序列化类
            -lookup_field:获取单挑数据的查询条件
            -filter_backends:过滤类
    	    -pagination_class:分页类
		-如何使用:
        	self:一个对象 等同于下面写的p
            self.queryset--->先找对象自己,而自己没有,才用了类的
            
         -方法:绑定给对象的方法(写成方法的目的是解耦合,提高扩展性)
            -get_queryset:返回所有要序列化的数据
            -get_object:获取单条数据,路由必须:path('/<int:pk>/', PublishDetailView.as_view()),
            -get_serializer:真正的实例化得到序列化类
            	ser=get_serializer(instance=qs,many=True)
            -get_serializer_class:仅仅是返回序列化类,不能传任何参数
            	serializer_class=self.get_serializer_class()
                serializer_class(instance=qs,many=True)
            	
    -以后,如果涉及到数据库操作,尽量用GenericAPIView

5个视图扩展类

# 写5个类(不叫视图类,视图扩展类,需要配合GenericAPIView一起用),每个类有一个方法,以后想写哪个接口,就继承哪个类即可
from rest_framework.response import Response

##### 咱们自己封装的

# class ListModelMixin:
#     def list(self, request, *args, **kwargs):
#         qs = self.get_queryset()
#         ser = self.get_serializer(qs, many=True)
#         return Response({'code': 100, 'msg': '成功', 'results': ser.data})
#
#
# class CreateModelMixin:
#     def create(self, request, *args, **kwargs):
#         ser = self.get_serializer(data=request.data)
#         if ser.is_valid():
#             ser.save()
#             return Response({'code': 100, 'msg': '成功'})
#         else:
#             return Response({'code': 100, 'msg': ser.errors})
#
#
# class RetrieveModelMixin:
#     def retrieve(self, request, *args, **kwargs):
#         book = self.get_object()
#         ser = self.get_serializer(book)
#         return Response({'code': 100, 'msg': '成功', 'results': ser.data})
#
#
# # DestroyModelMixin,UpdateModelMixin
# class DestroyModelMixin:
#     def destroy(self, request, *args, **kwargs):
#         self.get_object().delete()
#         return Response({'code': 100, 'msg': '删除成功'})
#
#
# class UpdateModelMixin:
#     def update(self, request, *args, **kwargs):
#         book = self.get_object()
#         ser = self.get_serializer(data=request.data, instance=book)
#         if ser.is_valid():
#             ser.save()
#             return Response({'code': 100, 'msg': '更新成功'})
#         else:
#             return Response({'code': 100, 'msg': ser.errors})


from .models import Book
from .serializer import BookSerialzier
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView

from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, \
    RetrieveModelMixin


class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)



class BookDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier

    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)
    
# 5 个视图扩展类--->不是视图类---》必须配合GenericAPIView及其子类使用---》不能配合APIView使用
# 5个视图扩展类,每一个类中只有一个方法,完成5个接口中的其中一个,想写多个接口,就要继承多个

9个视图子类

# ModelViewSet:
	-继承它后,只需要在视图类中写两行
    	queryset = Book.objects.all()
    	serializer_class = BookSerialzier
    -配置路由,5个接口都有了
    	path('books/', BookView.as_view({'get': 'list', 'post': 'create'})),
    	path('books/<int:pk>/', BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
        
        
        
# ModelViewSet 源码分析
	-继承了:      
    		mixins.CreateModelMixin,
             mixins.RetrieveModelMixin,
             mixins.UpdateModelMixin,
             mixins.DestroyModelMixin,
             mixins.ListModelMixin
             GenericViewSet
                -ViewSetMixin :没有见过,重写了 as_view
                -GenericAPIView
                
                
     -只要继承了ModelViewSet,路由写法变了,谁控制它变的:ViewSetMixin
   

# ViewSetMixin 如何控制路由写法变了?
	-BookView.as_view 是在执行,其实是ViewSetMixin的as_view
        @classonlymethod
        def as_view(cls, actions=None, **initkwargs):
            #我们传入的 actions={'get': 'list', 'post': 'create'}
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                for method, action in actions.items():
                    # method:get
                    # action:list
                    # 通过反射,self是BookView的对象,取BookView对象中反射list
                    handler = getattr(self, action)
                    # 通过反射:setattr(BookView的对象,'get',list方法)
                    setattr(self, method, handler)
                # APIViwe的dispatch
                return self.dispatch(request, *args, **kwargs)
            return csrf_exempt(view)
	
    
    
# 总结:
	'''
	1 只要继承了ViewSetMixin及其子类,路由写法就变了,必须传actions参数
	2 变成映射关系了:
		path('books/', BookView.as_view({'get': 'list', 'post': 'create'})),
    3 以后,只要是books路由匹配成功,的get请求,就会执行视图类BookView的list方法
    
    4 以后视图类中的方法名,可以随意命名
    5 这个类,必须配合视图类使用(APIView,GenericAPIView,9个视图子类),必须放在视图类之前
	'''

视图集

# 两个视图基类
	-APIView
    -GenericAPIView
# 5 个视图扩展类(不是视图类,需要配合GenericAPIView及其子类使用)
	
# 9个视图子类

# 视图集:
	ModelViewSet:5个接口的
    ReadOnlyModelViewSet:两个接口,list和retrieve
    ViewSetMixin:魔法,不能单独使用,必须配合视图类用,路由写法变了
    ViewSet:ViewSetMixin+APIView,以后想继承APIView,但是想路由写法变化,视图类中方法可以任意命名
    GenericViewSet:ViewSetMixin+GenericAPIView,以后想继承GenericAPIView,但是想路由写法变化,视图类中方法可以任意命名

drf之路由

# 路由写法有多种
	-原始写法
    -映射的写法:继承ModelViewSet,path('books/', BookView.as_view({'get': 'list', 'post': 'create'}))
    -自动生成路由
    
# 自动生成路由
	-必须要继承ViewSetMixin及其子类的视图类,才能用
    -继承了 5个视图扩展类+ViewSetMixin的视图类,能自动生成路由
    	-跟咱们写的这个是一样的
         -path('books/', BookView.as_view({'get': 'list', 'post': 'create'})),
   	     -path('books/<int:pk>/', BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
        
        
    -自己写的视图类的方法,如何映射
    	-映射的方式我们会
        -自动生成的方式
    	
        
        
        
# 自动生成路由
	1 继承了 5个视图扩展类+ViewSetMixin的视图类,能自动生成路由(get:list,get:retrieve..)
    2 我们自己命名的: 方法名:login  send_sms,需要使用装饰器来做
        # 视图类:
        class SMSView(ViewSet):
            @action(methods=['GET'], detail=False, url_path='lqz', url_name='lqz')
            def lqz(self, request):
        # 路由
        router.register('lqz',SMSView,'lqz')
        # 路径是:http://127.0.0.1:8000/api/v1/lqz/lqz/
        
   3 action装饰器的参数
	methods:请求方式
    detail:一个True,一个False,用True,表示生成详情的路径 <int:pk>==》前端查询单条,要设detail
    	# True,books/1/方法名/
        # False,books/方法名/
    url_path:路径名字,需要加上前面的路径一起,如果不加,默认以函数名作为路径名
    url_name:反向解析使用的名字(用的不多)
    
    
    
    
 # 路由类,有两个,用法完全一致,区别是DefaultRouter生成的路径多
	SimpleRouter :用的最多
    DefaultRouter
    # DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。

基于两个视图基类GenericAPIView,APIView

class Bookserializers(serializers.ModelSerializer):
    # name = serializers.CharField()
    # price = serializers.CharField()
    # publish_id = serializers.CharField(write_only=True)
    # Authors = serializers.ListField(write_only=True)
    Authors_list = serializers.ListField(read_only=True)
    publish_dict = serializers.DictField(read_only=True)

    class Meta:
        model = Book
        # feilds = "__all__"
        fields = ['name', 'price', 'publish', 'Authors', 'Authors_list', 'publish_dict']
        extra_kwargs = {

        }
class BookAPIView(APIView):
    def get(self, request):
        book = Book.objects.all()
        ser = Bookserializers(instance=book, many=True)
        return Response({"code": 200, "msg": "查询成功", "result": ser.data})

    def post(self, request):
        ser = Bookserializers(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({"code": 200, "msg": "增加成功", "result": ser.data})
        return Response({"code": 200, "msg": "增加失败", "result": ser.errors})


class BookDeaileAPIView(APIView):
    def get(self, request, *args, **kwargs):
        book = Book.objects.filter(pk=kwargs.get("pk")).first()
        ser = Bookserializers(instance=book, many=True)
        return Response({"code": 200, "msg": "查询成功", "result": ser.data})

    def put(self, request, *args, **kwargs):
        book = Book.objects.filter(pk=kwargs.get("pk")).first()
        ser = Bookserializers(instance=book, many=False, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({"code": 200, "msg": "修改成功", "result": ser.data})
        return Response({"code": 200, "msg": "修改败", "result": ser.errors})

    def delete(self, request, *args, **kwargs):
        Book.objects.filter(pk=kwargs.get("pk")).delete()

        return Response({"code": 200, "msg": "删除成功"})


class BookGenericAPIView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

    def get(self, request):
        book = self.get_queryset()
        ser = self.get_serializer(book, many=True)
        return Response({"code": 200, "msg": "查询成功", "result": ser.data})

    def post(self, request):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({"code": 200, "msg": "增加成功", "result": ser.data})
        return Response({"code": 200, "msg": "增加失败", "result": ser.errors})


class BookDeleailGenericAPIView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

    def get(self, request, *args, **kwargs):
        ser = self.get_serializer(self.get_object(), many=False)
        return Response({"code": 200, "msg": "查询成功", "result": ser.data})

    def put(self, request, *args, **kwargs):
        ser = self.get_serializer(self.get_object(), data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({"code": 200, "msg": "修改成功", "result": ser.data})
        return Response({"code": 200, "msg": "修改失败", "result": ser.errors})

    def delete(self, request, *args, **kwargs):
        self.get_object().delete()
        return Response({"code": 200, "msg": "删除成功"})

5个视图扩展类

 5个视图扩展类
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, \
    DestroyModelMixin


class ListModelMixin:
    def list(self, request):
        queryset = self.get_queryset()
        ser = self.get_serializer(queryset, many=True)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})


class CreateModelMixin:
    def create(self, request):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '成功', 'results': ser.data})
        return Response({'code': 100, 'msg': '失败', 'results': ser.errors})


class UpdateModelMixin:
    def update(self, request, *args, **kwargs):
        queryset = self.get_object()
        ser = self.get_serializer(queryset, many=False, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '成功', 'results': ser.data})
        return Response({'code': 100, 'msg': '失败', 'results': ser.errors})


class RetrieveModelMixin:
    def retrieve(self, request, *args, **kwargs):
        queryset = self.get_object()
        ser = self.get_serializer(queryset, many=False)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})


class DestroyModelMixin:
    def destroy(self, request, *args, **kwargs):
        self.get_object().delete()
        return Response({'code': 100, 'msg': '成功'})

9个视图子类

# 9个视图子类
class BookListGenericAPIView(GenericAPIView, ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

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


class BookCreateGenericAPIView(GenericAPIView, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

    def post(self, request):
        return self.create(request)


class BookRetrieveGenericAPIView(GenericAPIView, RetrieveModelMixin):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

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


class BookUpdateGenericAPIView(GenericAPIView, UpdateModelMixin):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

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


class BookDestroyGenericAPIView(GenericAPIView, DestroyModelMixin):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


class BookUpdateRetrieveDestroyGenericAPIView(GenericAPIView, DestroyModelMixin, RetrieveModelMixin, UpdateModelMixin):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

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

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


class BookListCreateGenericAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

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

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

视图集

from rest_framework.viewsets import ModelViewSet


# 继承五个视图扩展类,需要自己写路由
class BookModelViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = Bookserializers

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)

    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)
    
    
    
 	path('book/', BookModelViewSet.as_view({'get': 'list', 'post': 'create'})),
    path('book/<int:pk>/', BookModelViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),

自动生产路由

# ReadOnlyModelViewSet:两个接口,list和retrieve
from rest_framework.viewsets import ReadOnlyModelViewSet


class BookLIstretrieveView(ReadOnlyModelViewSet):
    queryset = Book.objects.all()
    serializer_class = Bookserializers


# ViewSetMixin:魔法,不能单独使用,必须配合视图类用,路由写法变了
from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action


class BookViewSet(ViewSet):  # ViewSetMixin, views.APIView
    queryset = Book.objects.all()
    serializer_class = Bookserializers
    ''''
    - 1 写在视图类方法上
        class SendView(ViewSet):
            # methods指定请求方法,可以传多个
            # detail:只能传True和False
                -False,不带id的路径:send/send_sms/
                -True,带id的路径:send/2/send_sms/
            #url_path:生成send后路径的名字,默认以方法名命名 
            # url_name:别名,反向解析使用,了解即可
            @action(methods=['POST'], detail=False)
            def send_sms(self, request):
    '''

    # @action(methods=['get'], detail=False)  # http://127.0.0.1:8000/lyx/lyx/
    @action(methods=['get'], detail=True)  # http://127.0.0.1:8000/lyx/lyx/
    def lyx(self, request, *args, **kwargs):  # http://127.0.0.1:8000/lyx/1/lyx/
        print(kwargs.get('pk'))
        return Response("你好")

from django.urls import path, include
from index.views import *
from rest_framework import routers

'''
SimpleRouter
DefaultRouter===>多生产一个跟路由
'''
router = routers.SimpleRouter()  # 创建一个 SimpleRouter 实例
router.register("book", BookLIstretrieveView)  # 注册视图集
router.register("lyx", BookViewSet)  # 注册视图集
'''
        prefix 该视图集的路由前缀
        viewset 视图集
        base_name 路由别名的前缀
'''  
urlpatterns = [
添加路由
    # 方式2
    path("", include(router.urls))
]
# 添加路由
# 方式一
# urlpatterns += router.urls
# http://127.0.0.1:8000/book/
# http://127.0.0.1:8000/book/<int:pk>/

drf视图

DRF(Django Rest Framework)是一个用于构建Web API的强大框架,提供了许多视图类来帮助我们快速构建API。下面是DRF中常用的视图关系:

GenericAPIView:这是一个视图类,它提供了一组通用的方法(如get、post、put、delete等),可以方便地处理HTTP请求。

ListAPIView:继承自GenericAPIView,用于返回查询集合的数据列表。

RetrieveAPIView:继承自GenericAPIView,用于返回单个对象实例的详细信息。

CreateAPIView:继承自GenericAPIView,用于创建新的对象实例。

UpdateAPIView:继承自GenericAPIView,用于更新现有的对象实例。

DestroyAPIView:继承自GenericAPIView,用于删除现有的对象实例。

ListCreateAPIView:继承自ListAPIView和CreateAPIView,用于同时处理列表和创建对象的请求。

RetrieveUpdateAPIView:继承自RetrieveAPIView和UpdateAPIView,用于同时处理获取单个对象实例和更新该实例的请求。

RetrieveDestroyAPIView:继承自RetrieveAPIView和DestroyAPIView,用于同时处理获取单个对象实例和删除该实例的请求。

RetrieveUpdateDestroyAPIView:继承自RetrieveAPIView、UpdateAPIView和DestroyAPIView,用于同时处理获取单个对象实例、更新该实例和删除该实例的请求。

ModelViewSet:继承自ViewSetMixin和GenericAPIView,用于提供CRUD操作的视图类。

ReadOnlyModelViewSet:继承自ViewsetMixin和GenericAPIView,用于提供只读操作(get、list、head等)的视图类。

ViewSet:继承自ViewSetMixin和GenericAPIView,用于实现非常定制化的API行为。

ModelViewset+ReadOnlyModelViewSet:继承自ModelViewSet和ReadOnlyModelViewSet,用于同时提供CRUD和只读操作的视图类。

认证

# 认证,在执行视图之前执行了三大认证
# 认证的使用方法
'''
1 写一个类,继承BaseAuthentication
2 在类中写authenticate方法
3 在方法中,完成登录认证,如果不是登录,抛异常
     token = request.GET.get("token")
        user_taken = UserTaken.objects.filter(token=token).first()
        if not user_taken:
            raise AuthenticationFailed("你暂时没有登录")
4 如果登录成功,返回登录用户和token
    return user_taken.user, token
5 在视图类中使用,(局部使用)
 authentication_classes = [UserAuthentication]  # 局部
6 全局使用
    REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        '应用名.模块名.类名',  # 认证
    ],
}
7 全局使用后,局部禁用
    REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        '应用名.模块名.类名',  # 认证
    ],
}
    局部禁用
    authentication_classes = []
8 认证类的使用顺序
    -先使用局部
    -在使用项目配置文件
    -使用drf默认配置

重点,一旦通过了认证,在request中就有当前登录用户



'''''

权限

'''
# 权限类使用
## 1 写一个类,继承BasePermission
### 2 在类中写方法:has_permission
    1 如果有权限,返回True
            if request.user.is_super == 1:
            return True
    2 如果没有权限,返回False
            return False
    3 错误信息是self.message=""
            self.message = ""
            return False
### 3 局部使用
 permission_classes = [UThrottle]
### 4 全局使用
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'index.UserAuthentication.UserAuthentication',  # 认证
        ],
        'DEFAULT_PERMISSION_CLASSES': [
    
            'index.userthrottle.BasePermission'
        ],
    }
### 5 局部禁用
permission_classes = []

频率

'''
频率,按ip地址,用户id,限制
### 1 写一个类,继承SimpleRateThrottle
### 2 写一个方法,get_cache_key
    -可以返回ip或者用户id
     return request.META.get("REMOTE_ADDR")
    -返回什么,就用什么做频率限制
### 3 写一个类属性
    scope='lqz'
### 4 在配置文件中配置
    'DEFAULT_THROTTLE_RATES': {
            'lqz': '3/m' # 一分钟访问3次
        },
### 5 全局使用
 'DEFAULT_THROTTLE_CLASSES': [

        ],
### 6 局部使用
 class BookView(APIView):
        throttle_classes = [MyThrottle]

'''
# 1 权限源码
	-继承了APIView----》dispatch----》407行左右---》 self.initial(request, *args, **kwargs)---》414行 ---》self.check_permissions(request)
    -def check_permissions(self, request):
        # 自己写的配置的权限类的对象
        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)
                )
                
                
# 2 认证类的源码
    -继承了APIView----》dispatch----》407行左右---》 self.initial(request, *args, **kwargs)---》414行 ---》self.perform_authentication(request)---》
    # perform_authentication
        def perform_authentication(self, request):
            request.user

    # request是新的Request----》from rest_framework.request import Request----》220行
        def user(self):
            if not hasattr(self, '_user'):
                with wrap_attributeerrors():
                    self._authenticate()
            return self._user

    #  request是新的Request--->self._authenticate()---》373行
            def _authenticate(self):
                # self.authenticators配置再视图类上的认证类对象列表[认证类对象1,认证类对象2]
                for authenticator in self.authenticators:
                    try:
                        user_auth_tuple = authenticator.authenticate(self)
                    except exceptions.APIException: # drf中所有的异常都继承了APIException
                        self._not_authenticated()
                        raise

                    if user_auth_tuple is not None:
                        self._authenticator = authenticator
                        self.user, self.auth = user_auth_tuple # 解压赋值
                        return

                self._not_authenticated()
     # request.user---->能拿到当前登录用户
    
    
    
 # 3 排序---》查询所有---》继承GenericAPIView及其子类的
	-filter_backends=[排序类]  # 内置有,不需要自己写
    - ordering_fields = ['price','id']
    -http://127.0.0.1:8000/api/v1/books/?ordering=-price,-id
    
# 4 过滤
	-内置过滤类
    -第三方过滤类:django-filter
    -自定义过滤类:
    	-写一个类,继承BaseFilterBackend
        -重写:filter_queryset,在里面完成过滤,返回qs对象
        
    -我们猜:ListModelMixin---》list,执行了配置的过滤类的filter_queryset方法
        def list(self, request, *args, **kwargs):
            # queryset = self.filter_queryset(所有数据)
            # filter_queryset肯定执行了我们自己写的过滤类
            queryset = self.filter_queryset(self.get_queryset())
            page = self.paginate_queryset(queryset)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)

            serializer = self.get_serializer(queryset, many=True)
            return Response(serializer.data)
 
# 5 分页
	-PageNumberPagination, LimitOffsetPagination, CursorPagination
# 上下文管理器
	-__enter__
    -__exit__
    
# 面向对象封装---》
	-__开头,隐藏了,
    	-内部使用self.__属性/方法
        -外部使用:person._类名__字段名
    -别人,公司内部,不太使用 __
    	-约定俗称,类内部使用 _开头,本意就是隐藏,不给外部使用

全局异常处理

# 只要三大认证,视图类的方法出了异常,都会执行一个函数:rest_framework.views import exception_handler

### 注意:exception_handler
    # 如果异常对象是drf的APIException对象,就会返回Response
    # exception_handler只处理了drf的异常,其它的异常需要我们自己处理
    # 如果异常对象不是drf的APIException对象,就会返回None
# 补充:
    # isinstance()   判断一个对象是不是某个类的对象  isinstance(对象,类)
    # issubclass()   判断一个类,是不是另一个类的子类
    
    
    
def common_exception_handler(exc, context):
    # 只要走到这里,一定出异常了,我们正常的项目要记录日志(后面讲)
    # 两种可能:一个是Response对象,一个是None
    res = exception_handler(exc, context)
    if res:
        # 说明是drf的异常,它处理了
        if isinstance(res.data, dict):
            detail = res.data.get('detail')
        else:
            detail = res.data
        return Response({'code': 998, 'msg': detail})
    else:
        # 说明是其它异常,它没有处理
        # return Response({'code': 999, 'msg': '系统异常,请联系系统管理员'})
        return Response({'code': 999, 'msg': str(exc)})
    
    
### 配置文件
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'app01.excepitons.common_exception_handler',
}

接口文档

# 后端把接口写好后
	-登录接口
    -注册接口
    -查询所有图书带过滤接口
# 前端人员需要根据接口文档,进行前端开发

# 前后端需要做对接----》对接第一个东西就是这个接口文档---》前端照着接口文档开发

# 公司3个人,每个人开发了10个接口,3个人都要同时写接口文档

# 接口文档的编写形式
	-1 world,md,编写,大家都可以操作,写完放在git,公司的文档管理平台上
    -2 第三方的接口文档平台(收费)
    	https://www.showdoc.com.cn/
    -3 公司自己开发接口文档平台
    -4 公司使用开源的接口文档平台,搭建
    	-YAPI:百度开源的
        -https://zhuanlan.zhihu.com/p/366025001
        
    -5 项目自动生成接口文档
    	-coreapi
        -swagger
        
        
# 使用coreapi自动生成接口文档
	-使用步骤:
    	-1 安装:pip3 install coreapi
		-2 加一个路由
        	from rest_framework.documentation import include_docs_urls	
        	urlpatterns = [
            	path('docs/', include_docs_urls(title='站点页面标题'))
        	]
        -3 在视图类上加注释
        
        -4 配置文件中配置:
        'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
        -5 表模型或序列化类的字段上写 help_text--->会显示在接口文档的字段介绍上
        
        -6 访问地址:
        http://127.0.0.1:8000/docs/
        

jwt介绍和原理

# cookie,session,token发展历史
	-会话管理
    -cookie:客户端浏览器的键值对
    -session:服务的的键值对(djangosession表,内存中,文件,缓存数据库)
    -token:服务的生成的加密字符串,如果存在客户端浏览器上,就叫cookie
    	-三部分:头,荷载,签名
        -签发:登录成功,签发
        -认证:认证类中认证
    
# jwt:Json web token (JWT),web方向的token认证

base64编码和解码

# base64并不是一种加密反射,只是编码解码方式
#  字符串,可以转成base64编码格式:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 解码成base64



import json
import base64

 d = {'user_id': 1, 'username': "lqz"}

d_str = json.dumps(d)
# print(d_str)
# # 对字符串进行bashe64 编码
# res=base64.b64encode(bytes(d_str,encoding='utf-8'))
# print(res)  # eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImxxeiJ9


# 解码
# res=base64.b64decode('TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ=')
# print(res)


# 记住: base64 编码,长度一定是4的倍数。如果不够,用 = 补齐


# base64的用途
'''
1 互联网中,前后端数据交互,使用base64编码
2 jwt 字符串使用base64编码
3 互联网中一些图片,使用base64编码

'''

import json
import base64

l = {"code": 111, "msg": "你好你好"}
l_str = json.dumps(l)
l_str = l_str.encode('utf-8')
res = base64.b64encode(l_str)
print(res)  # b'eyJjb2RlIjogMTExLCAibXNnIjogIlx1NGY2MFx1NTk3ZFx1NGY2MFx1NTk3ZCJ9'
m = base64.b64decode(res)
print(m)  # b'{"code": 111, "msg": "\\u4f60\\u597d\\u4f60\\u597d"}'
print(json.loads(m))  # {'code': 111, 'msg': '你好你好'}
print(m.decode('utf-8'))  # {"code": 111, "msg": "\u4f60\u597d\u4f60\u597d"}

jwt原理

https://www.cnblogs.com/liuqingzheng/articles/17413681.html

三段式

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

#1  header  jwt的头部承载两部分信息:
声明类型,这里是jwt
声明加密的算法 通常直接使用 HMAC SHA256
公司信息
{
  'typ': 'JWT',
  'alg': 'HS256'
}
然后将头部进行base64编码,构成了第一部分.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

# 2 payload 载荷就是存放有效信息的地方。
当前用户信息,用户名,用户id
exp: jwt的过期时间,这个过期时间必须要大于签发时间

定义一个payload:
{
  "exp": "1234567890",
  "name": "John Doe",
  "user_id":99
}
然后将其进行base64编码,得到JWT的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9


# signature  JWT的第三部分是一个签证信息,这个签证信息由三部分组成:

header (base64后的)
payload (base64后的)
secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

jwt开发流程

# 使用jwt认证的开发流程,就是两部分
	-第一部分:签发token的过程,登录做的
    	-用户携带用户名和密码,访问我,我们校验通过,生成token串,返回给前端
    -第二部分:token认证过程,登录认证时使用,其实就是咱们之前讲的认证类,在认证类中完成对token的认证操作
    	-用户访问我们需要登陆后才能访问的接口,必须携带我们签发的token串(请求头)
        -我们取出token,验证该token是否过期,是否被篡改,是否是伪造的
        -如果正常,说明荷载中的数据,就是安全的,可以根据荷载中的用户id,查询出当前登录用户,放到request中即可

drf-jwt

# djagno+drf框架中,使用jwt来做登录认证

# 使用第三方:
	-django-rest-framework-jwt:https://github.com/jpadilla/django-rest-framework-jwt
    -djangorestframework-simplejwt:https://github.com/jazzband/djangorestframework-simplejwt
    
# 我们可以自己封装 :https://gitee.com/liuqingzheng/rbac_manager/tree/master/libs/lqz_jwt


# 安装
	pip3 install djangorestframework-jwt
    
    
# 补充:
	- 密码明文一样,每次加密后都不一样,如何做,动态加盐,动态的盐要保存,跟密码保存在一起
    - 有代码,有数据库,没有登录不进去的系统
    
# 解决不了:
	token被获取,模拟发送请求
    不能篡改
    不能伪造
# 快速使用
	-签发过程(快速签发),必须是auth的user表
    	-登录接口--》基于auth的user表签发的
    -认证过程
    	-登录认证,认证类
        
        
        
# 总结:
	-签发:只需要在路由中配置
        from rest_framework_jwt.views import obtain_jwt_token
        urlpatterns = [
            path('login/', obtain_jwt_token), 
        ]
    -认证:视图类上加
    	class BookView(APIView):
            authentication_classes = [JSONWebTokenAuthentication] # 认证类,drf-jwt提供的
            permission_classes = [IsAuthenticated] # 权限类,drf提供的
    -访问的时候,要在请求头中携带,必须叫
    	Authorization:jwt token串

drf-jwt定制返回格式

# 登录签发token的接口,要返回code,msg,username,token等信息

# 对返回格式进行定制
	-1 写个函数,函数返回字典格式,返回的格式,会被序列化,前端看到	
    	def common_response(token, user=None, request=None):
            return {
                'code': '100',
                'msg': '登录成功',
                'username': user.username,
                'token': token,
            }

    -2 写的函数配置一下
        JWT_AUTH = {
        'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.jwt_response.common_response',
    }

drf-jwt自定义用户签发

# 1 创建一个用户表
	class User(models.Model):
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=64)
        age = models.IntegerField()
        email = models.CharField(max_length=64)
# 2 登录接口
	class UserView(ViewSet):
        def login(self, request):
            username = request.data.get('username')
            password = request.data.get('password')
            user = User.objects.filter(username=username, password=password).first()
            if user:
                # 登录成功,签发token--->先背过,
                # 1 通过user,获取payload
                payload = jwt_payload_handler(user)
                print(payload)
                # 2 通过payload,得到token
                token = jwt_encode_handler(payload)
                return Response({'code': 100, 'msg': '登录成功', 'username': user.username, 'token': token})
            else:
                return Response({'code': 101, 'msg': '用户名或密码错误'})
# 3 登录接口签发token返回给前端

drf-jwt自定义认证类

'''
认证,成功返回用户和token,否则抛异常
'''


# drf的认证类定义方式,之前学过
# 在认证类中,自己写逻辑

class JWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.META.get('HTTP_TOKEN')
        # 背过
        # 校验token是否过期,合法,如果都通过,查询出当前用户,返回
        # 如果不通过,抛异常
        try:
            payload = jwt_decode_handler(token)
            # 如果认证通过,payload就可以认为是安全的,我们就可以使用
            user_id = payload.get('user_id')
            # 每个需要登录后,才能访问的接口,都会走这个认证类,一旦走到这个认证类,机会去数据库查询一次数据,会对数据造成压力?
            user = User.objects.get(pk=user_id)
            # 优化后的
            # user = User(username=payload.get('username'), id=user_id)
            # user = {'username':payload.get('username'), 'id':user_id}

        except jwt.ExpiredSignature:
            raise AuthenticationFailed('token过期')
        except jwt.DecodeError:
            raise AuthenticationFailed('解码失败')
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('token认证异常')
        except Exception:
            raise AuthenticationFailed('token认证异常')
        return user, token
    
    
    
    
  # 全局使用,局部使用




class Auth(BaseAuthentication):
    def authenticate(self, request):
        token = request.META.get('HTTP_TOKEN')
        try:
            payload = jwt_decode_handler(token)
            user = {"username": payload.get("username"), "id": payload.get("user_id")}
        except Exception:
            raise AuthenticationFailed("认证失败")
        return user, token

drf-jwt的签发源码分析

# from rest_framework_jwt.views import obtain_jwt_token
# obtain_jwt_token就是ObtainJSONWebToken.as_view()---》视图类.as_view()

# 视图类
class ObtainJSONWebToken(JSONWebTokenAPIView):
    serializer_class = JSONWebTokenSerializer
    
# 父类:JSONWebTokenAPIView
class JSONWebTokenAPIView(APIView):
    # 局部禁用掉权限和认证
    permission_classes = ()
    authentication_classes = ()
    def post(self, request, *args, **kwargs):
        # serializer=JSONWebTokenSerializer(data=request.data)
        serializer = self.get_serializer(data=request.data)
		# 调用序列化列的is_valid---》字段自己的校验规则,局部钩子和全局钩子
        # 读JSONWebTokenSerializer的局部或全局钩子
        if serializer.is_valid(): # 全局钩子在校验用户,生成token
            # 从序列化类中取出user
            user = serializer.object.get('user') or request.user
            user = serializer.object.get('user') or request.user
            # 从序列化类中取出token
            token = serializer.object.get('token')
            # 咱么定制返回格式的时候,写的就是这个函数
            response_data = jwt_response_payload_handler(token, user, request)
            response = Response(response_data)
            return response

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    
    
    
# JSONWebTokenSerializer的全局钩子
class JSONWebTokenSerializer(Serializer):
    def validate(self, attrs):
        # attrs前端传入的,校验过后的数据 {username:lqz,password:lqz12345}
        credentials = {
            'username': attrs.get('usernme'),
            'password': attrs.get('password')
        }

        if all(credentials.values()): # 校验credentials中字典的value值是否都不为空
            # user=authenticate(username=前端传入的,password=前端传入的)
            # auth模块的用户名密码认证函数,可以传入用户名密码,去auth的user表中校验用户是否存在
            # 等同于:User.object.filter(username=username,password=加密后的密码).first()
            user = authenticate(**credentials)
            if user:
                if not user.is_active:
                    msg = _('User account is disabled.')
                    raise serializers.ValidationError(msg)

                payload = jwt_payload_handler(user)

                return {
                    'token': jwt_encode_handler(payload),
                    'user': user
                }
            else:
                msg = _('Unable to log in with provided credentials.')
                raise serializers.ValidationError(msg)
        else:
            msg = _('Must include "{username_field}" and "password".')
            msg = msg.format(username_field=self.username_field)
            raise serializers.ValidationError(msg)

认证源码

# from rest_framework_jwt.authentication import JSONWebTokenAuthentication
# JSONWebTokenAuthentication
class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
    def get_jwt_value(self, request):
        # auth=['jwt','token串']
        auth = get_authorization_header(request).split()
        auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()
        if not auth:
            if api_settings.JWT_AUTH_COOKIE:
                return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
            return None
        if smart_text(auth[0].lower()) != auth_header_prefix:
            return None
        if len(auth) == 1:
            msg = _('Invalid Authorization header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _('Invalid Authorization header. Credentials string '
                    'should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)
        return auth[1] # token串
    
# 父类中找:BaseJSONWebTokenAuthentication---》authenticate,找到了
class BaseJSONWebTokenAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 拿到前端传入的token,前端传入的样子是  jwt token串
        jwt_value = self.get_jwt_value(request)
        # 如果前端没传,返回None,request.user中就没有当前登录用户
        # 如果前端没有携带token,也能进入到视图类的方法中执行,控制不住登录用户
        # 所以咱们才加了个权限类,来做控制
        if jwt_value is None:
            return None
        try:
            payload = jwt_decode_handler(jwt_value)
        except jwt.ExpiredSignature:
            msg = _('Signature has expired.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = _('Error decoding signature.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed()
        # 通过payload拿到当前登录用户
        user = self.authenticate_credentials(payload)
        return (user, jwt_value)

    
    
# 如果用户不携带token,也能认证通过
# 所以我们必须加个权限类来限制

class IsAuthenticated(BasePermission):
    def has_permission(self, request, view):
        return bool(request.user and request.user.is_authenticated)

django-simpleui使用

安装

pip install django-simpleui

豆瓣:http://pypi.douban.com/simple/
中科大:https://pypi.mirrors.ustc.edu.cn/simple/
清华:https://pypi.tuna.tsinghua.edu.cn/simple/

settings配置

INSTALLED_APPS = [
    'simpleui',
]

升级simpleui

pip install django-simpleui --upgrade

修改主题

SIMPLEUI_DEFAULT_THEME = 'admin.lte.css'#默认的==》有Simpleui-x,layui等

图标

SIMPLEUI_DEFAULT_ICON = False

True	开启默认图标,默认为True
False	关闭默认图标


#自定义图标
SIMPLEUI_ICON = {
    '系统管理': 'fab fa-apple',
    '员工管理': 'fas fa-user-tie'
}

菜单

system_keep 保留系统菜单   默认为False,不保留。 如果改为True,自定义和系统菜单将会并存
menu_display 过滤显示菜单和排序功能
dynamic 开启动态菜单功能

例子

import time
SIMPLEUI_CONFIG = {
    'system_keep': False,
    'menu_display': ['Simpleui', '测试', '权限认证', '动态菜单测试'],      # 开启排序和过滤功能, 不填此字段为默认排序和全部显示, 空列表[] 为全部不显示.
    'dynamic': True,    # 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时动态展示菜单内容
    'menus': [{
        'name': 'Simpleui',
        'icon': 'fas fa-code',
        'url': 'https://gitee.com/tompeppa/simpleui',
        # 浏览器新标签中打开
        'newTab': True,
    }, {
        'app': 'auth',
        'name': '权限认证',
        'icon': 'fas fa-user-shield',
        'models': [{
            'name': '用户',
            'icon': 'fa fa-user',
            'url': 'auth/user/'
        }]
    }, {
        # 自2021.02.01+ 支持多级菜单,models 为子菜单名
        'name': '多级菜单测试',
        'icon': 'fa fa-file',
      	# 二级菜单
        'models': [{
            'name': 'Baidu',
            'icon': 'far fa-surprise',
            # 第三级菜单 ,
            'models': [
                {
                  'name': '爱奇艺',
                  'url': 'https://www.iqiyi.com/dianshiju/'
                  # 第四级就不支持了,element只支持了3级
                }, {
                    'name': '百度问答',
                    'icon': 'far fa-surprise',
                    'url': 'https://zhidao.baidu.com/'
                }
            ]
        }, {
            'name': '内网穿透',
            'url': 'https://www.wezoz.com',
            'icon': 'fab fa-github'
        }]
    }, {
        'name': '动态菜单测试' ,
        'icon': 'fa fa-desktop',
        'models': [{
            'name': time.time(),
            'url': 'http://baidu.com',
            'icon': 'far fa-surprise'
        }]
    }]
}

模板

修改模板修改模板

重写首页,在templates目录中新建admin文件夹,然后添加index.html 如果选择继承方式,就只能采用block

 {% extends 'admin/index.html' %}
    {% load static %}

    {% block head %}
        {{ block.super }}
        ..此处写你的代码
    {% endblock %}

    {% block script %}
        {{ block.super }}
        ..此处写你的代码
    {% endblock %}

全部重写

<html>
    <head>
        <title>完全自定义</title>
    </head>
    <body>
        这里你是自定义的html代码
    </body>
</html>

头部添加自定义代码

   {% extends 'admin/index.html' %}
    {% load static %}

    {% block head %}
        {{ block.super }}
        ..此处写你的代码
    {% endblock %}

底部添加自定义代码

    {% extends 'admin/index.html' %}
    {% load static %}

    {% block script %}
        {{ block.super }}
        ..此处写你的代码
    {% endblock %}

自定义按钮

 @admin.register(Employe)
class EmployeAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'gender')
   
    # 增加自定义按钮
    actions = ['make_copy', 'custom_button']

    def custom_button(self, request, queryset):
        pass

    # 显示的文本,与django admin一致
    custom_button.short_description = '测试按钮'
    # icon,参考element-ui icon与https://fontawesome.com
    custom_button.icon = 'fas fa-audio-description'

    # 指定element-ui的按钮类型,参考https://element.eleme.cn/#/zh-CN/component/button
    custom_button.type = 'danger'

    # 给按钮追加自定义的颜色
    custom_button.style = 'color:black;'

    def make_copy(self, request, queryset):
        pass
    make_copy.short_description = '复制员工'

字段参数

字段	说明
icon	按钮图标,参考https://element.eleme.cn/#/zh-CN/component/icon与https://fontawesome.com,把class 复制进来即可
type	按钮类型,参考:https://element.eleme.cn/#/zh-CN/component/button
style	自定义css样式
confirm	弹出确认框,在3.4或以上版本中生效

def message_test(self, request, queryset):
        messages.add_message(request, messages.SUCCESS, '操作成功123123123123')
        
    # 给按钮增加确认
    message_test.confirm = '你是否执意要点击这个按钮?'

链接按钮

action_type	按钮动作类型,0=当前页内打开,1=新tab打开,2=浏览器tab打开
action_url	按钮访问链接

# 增加自定义按钮
    actions = ['custom_button']

    def custom_button(self, request, queryset):
        pass

    # 显示的文本,与django admin一致
    custom_button.short_description = '测试按钮'
    # icon,参考element-ui icon与https://fontawesome.com
    custom_button.icon = 'fas fa-audio-description'

    # 指定element-ui的按钮类型,参考https://element.eleme.cn/#/zh-CN/component/button
    custom_button.type = 'danger'

    # 给按钮追加自定义的颜色
    custom_button.style = 'color:black;'

    # 链接按钮,设置之后直接访问该链接
    # 3中打开方式
    # action_type 0=当前页内打开,1=新tab打开,2=浏览器tab打开
    # 设置了action_type,不设置url,页面内将报错
    # 设置成链接类型的按钮后,custom_button方法将不会执行。

    custom_button.action_type = 0
    custom_button.action_url = 'http://www.baidu.com'

layer对话框按钮

#对话框按钮是在admin中进行配置action,可以自定义输入的字段,然后通过ajax请求到action中进行业务的处理。

#需要继承AjaxAdmin 在from simpleui.admin import AjaxAdmin包中simplepro也会同步支持对话框按钮功能


字段说明

下列字段是指actionlayer属性

字段 说明
title 对话框标题
tips 对话框提示
confirm_button 确认按钮文本
cancel_button 取消按钮文本
width 对话框宽度,百分比,例如:50%
labelWidth 表格的label宽度,例如:80px
params 对话框表格中的字段,array
params字段
字段 说明
type 类型,取值为:input原生属性,和elementui的:select、date、datetime、rate、color、slider、switch、input_number、checkbox、radio
key 参数名,post参数中获取的名称
value 默认值,数组或文本
label 字段在表格中显示的名称
size 组件的大小,取值为:medium / small / mini
require 是否必选,取值为:True/False
width 输入框宽度,例如:200px
options 选项,数组,type为select、checkbox、radio的时候可用

字段options字段

字段 说明
key
label 显示文本

例子

class RecordAdmin(ImportExportActionModelAdmin, AjaxAdmin):
    resource_class = ProxyResource

    list_display = ('id', 'name', 'type', 'money', 'create_date')
    list_per_page = 10

    actions = ('layer_input',)

    def layer_input(self, request, queryset):
        # 这里的queryset 会有数据过滤,只包含选中的数据

        post = request.POST
        # 这里获取到数据后,可以做些业务处理
        # post中的_action 是方法名
        # post中 _selected 是选中的数据,逗号分割
        if not post.get('_selected'):
            return JsonResponse(data={
                'status': 'error',
                'msg': '请先选中数据!'
            })
        else:
            return JsonResponse(data={
                'status': 'success',
                'msg': '处理成功!'
            })

    layer_input.short_description = '弹出对话框输入'
    layer_input.type = 'success'
    layer_input.icon = 'el-icon-s-promotion'

    # 指定一个输入参数,应该是一个数组

    # 指定为弹出层,这个参数最关键
    layer_input.layer = {
        # 弹出层中的输入框配置

        # 这里指定对话框的标题
        'title': '弹出层输入框',
        # 提示信息
        'tips': '这个弹出对话框是需要在admin中进行定义,数据新增编辑等功能,需要自己来实现。',
        # 确认按钮显示文本
        'confirm_button': '确认提交',
        # 取消按钮显示文本
        'cancel_button': '取消',

        # 弹出层对话框的宽度,默认50%
        'width': '40%',

        # 表单中 label的宽度,对应element-ui的 label-width,默认80px
        'labelWidth': "80px",
        'params': [{
            # 这里的type 对应el-input的原生input属性,默认为input
            'type': 'input',
            # key 对应post参数中的key
            'key': 'name',
            # 显示的文本
            'label': '名称',
            # 为空校验,默认为False
            'require': True
        }, {
            'type': 'select',
            'key': 'type',
            'label': '类型',
            'width': '200px',
            # size对应elementui的size,取值为:medium / small / mini
            'size': 'small',
            # value字段可以指定默认值
            'value': '0',
            'options': [{
                'key': '0',
                'label': '收入'
            }, {
                'key': '1',
                'label': '支出'
            }]
        }, {
            'type': 'number',
            'key': 'money',
            'label': '金额',
            # 设置默认值
            'value': 1000
        }, {
            'type': 'date',
            'key': 'date',
            'label': '日期',
        }, {
            'type': 'datetime',
            'key': 'datetime',
            'label': '时间',
        }, {
            'type': 'rate',
            'key': 'star',
            'label': '评价等级'
        }, {
            'type': 'color',
            'key': 'color',
            'label': '颜色'
        }, {
            'type': 'slider',
            'key': 'slider',
            'label': '滑块'
        }, {
            'type': 'switch',
            'key': 'switch',
            'label': 'switch开关'
        }, {
            'type': 'input_number',
            'key': 'input_number',
            'label': 'input number'
        }, {
            'type': 'checkbox',
            'key': 'checkbox',
            # 必须指定默认值
            'value': [],
            'label': '复选框',
            'options': [{
                'key': '0',
                'label': '收入'
            }]
        }]
    }

action 返回结果

{
    'status': 'error',
    'msg': '请先选中数据!'
}

layer 文件上传

@admin.register(Layer)
class LayerAdmin(AjaxAdmin):
    actions = ('upload_file',)

    def upload_file(self, request, queryset):
        # 这里的upload 就是和params中配置的key一样
        upload= request.FILES['upload']
        print(upload)
        pass

    upload_file.short_description = '文件上传对话框'
    upload_file.type = 'success'
    upload_file.icon = 'el-icon-upload'
    upload_file.enable = True

    upload_file.layer = {
        'params': [{
            'type': 'file',
            'key': 'upload',
            'label': '文件'
        }]
    }

关闭登录动画

SIMPLEUI_LOGIN_PARTICLES = False

首页-修改默认

首页配置
SIMPLEUI_HOME_PAGE = 'https://www.baidu.com'

首页标题
SIMPLEUI_HOME_TITLE = '百度一下你就知道'

首页图标,支持element-ui和fontawesome的图标,参考https://fontawesome.com/icons图标
SIMPLEUI_HOME_ICON = 'fa fa-user'

首页-跳转地址

# 设置simpleui 点击首页图标跳转的地址
SIMPLEUI_INDEX = 'https://www.baidu.com'
SIMPLEUI_LOGO = 'https://avatars2.githubusercontent.com/u/13655483?s=60&v=4'

服务器信息

隐藏:

SIMPLEUI_HOME_INFO = False

显示:

SIMPLEUI_HOME_INFO = True

快速操作

隐藏:

SIMPLEUI_HOME_QUICK = False

显示:

SIMPLEUI_HOME_QUICK = True

最近动作

隐藏:

SIMPLEUI_HOME_ACTION = False

显示:

SIMPLEUI_HOME_ACTION = True

离线模式

SIMPLEUI_STATIC_OFFLINE = True

标签:return,get,self,request,data,def,drf
From: https://www.cnblogs.com/liyuanxiangls/p/17445504.html

相关文章

  • drf-基于角色的访问控制
    目录一权限控制ACLRBAC二控制权限2.1django中,如何做权限控制的2.2我们开发中做权限控制2.3基于django的auth+admin+第三方美化快速开发1.安装2.配置文件配置,注册app3.菜单栏定制4.自定义菜单和页面5.自定义按钮6.切换图标7.首页显示的隐藏2.4djagno-vue-admin一权......
  • drf-JWT认证
    目录一jwt介绍和原理1.1cookie,session,token发展历史1.2认证过程1.3JWT的构成1.3.1header1.3.2payload1.3.3signature二base64编码和解码2.1base64使用2.2base64的用途三jwt开发流程四drf-jwt快速使用五drf-jwt定制返回格式六自定义签发和认证6.1drf-jwt自定义用户......
  • drf-jwt
    jwt原理使用jwt认证和使用session认证的区别session是将用户信息存放在数据库中,在客户端存一个随机的字符串,下次用户提交的时候将字符串与数据库中存在的进行比较jwt是根据用户提交的信息,产生一个token,将token返回给客户端,下次用户提交的时候,将token进行解析三段式#这是一......
  • drf——jwt
    jwt原理使用jwt认证和使用session认证的区别三段式eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ#1.headerjwt的头部承载两部分信息:声明类型这里是jwt......
  • jwt原理,jwt开发流程,drf-jwt快速使用,drf-jwt定制返回格式,drf-jwt自定义用户表签发,drf-j
    jwt原理:  JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串1headerjwt的头部承载两部分信息:声明类型,这里是jwt声明加密的算法通常直接使用HMACSHA256公司信息......
  • jwt原理开发,drf-jwt快速使用和自定义使用,jwt签发认证源码分析
    一眼弄懂cookieSeesiontoken区别彻底弄懂cookie,session和token区别1jwt原理1.1使用jwt认证和使用session认证的区别1.2三段式eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEf......
  • DRF之通过GenericAPIView的视图子类实现数据的增删改查接口
    1、安装DRFpipinstalldjangorestframework2、将DRF注册到APP中INSTALLED_APPS=['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','djan......
  • DRF学习第4课
    1,认证和权限的使用在settings.py配置,可全局,也可以单独针对某个类#AllowAny允许所有用户#IsAuthenticated仅通过认证的用户#IsAdminUser仅管理员用户#IsAuthenticatedOrReadOnly认证的用户可以完全操作,否则只能get读取2,限流1,用户限流2,视图限流3,过滤Filteringpipi......
  • DRF学习第三课
    Restful接口标准:1,请求方式:获取GET,保存POST,更新PUT,删除DELETE2,请求路径:1,资源的名词复数作为路径 2,单一资源操作如:books/1/3,请求参数:1,根据模型确定传递的字段-保存或更新--JSON 2,过滤信息--查询字符串形式传递4,返回结果:1,错误{error:错误信息}2,正确 根据请求方式返回......
  • DRF学习第二课,实战
    1,models模型classBookInfo(models.Model):title=models.CharField(verbose_name="图书名称",max_length=32)pub_date=models.DateField(verbose_name="发布日期")read=models.IntegerField(default=0,verbose_name="阅读量")comment=......