05限流和序列化

news/2024/9/20 14:25:48

限流和序列化

1.限流

限流,限制用户访问频率,一般的限流用户信息都存于缓存之中,例如:用户1分钟最多访问100次 或者 短信验证码一天每天可以发送50次, 防止盗刷。

  • 对于匿名用户,使用用户IP作为唯一标识。
  • 对于登录用户,使用用户ID或名称作为唯一标识。

1.1限流组件使用步骤

(1)创建限流组件类
# 引入相关模块
from rest_framework import exceptions
from rest_framework import status
from rest_framework.throttling import SimpleRateThrottleclass CommonThrottle(SimpleRateThrottle):cache = default_cache  # 访问记录存放在django的缓存中(需设置缓存)scope = "user"  # 构造缓存中的key# 唯一标识符cache_format = 'throttle_%(scope)s_%(ident)s'# 设置访问频率,例如:1分钟允许访问10次# 其他:'s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day'THROTTLE_RATES = {"user": "10/m"}# 获取用户信息,组成唯一标识def get_cache_key(self, request, view):# 返回什么,就会以什么做限制,以ip地址限制return request.META.get('REMOTE_ADDR')# 超出限制提示信息方法def throttle_failure(self):# 调用父类wait方法,得到需要等待的时间wait = self.wait()# 异常信息detail = {"code": 1005,"data": "访问频率限制",'detail': "需等待{}s才能访问".format(int(wait))}# 抛出异常raise ThrottledException(detail)
  • 创建了限流类CommonThrottle,继承SimpleRateThrottle

  • 访问记录全部存放在django的缓存中

  • THROTTLE_RATES = {"user": "10/m"}设置访问频率

  • 如果超过限流限制,走throttle_failure方法提示异常信息

(2)调用限流类
class OrderView(APIView):# 声明throttle_classes的值为限流类名throttle_classes = [CommonThrottle, ]def get(self, request):return Response({"code": 0, "data": "数据..."})

1.2 多个限流类

本质,每个限流的类中都有一个 allow_request 方法,此方法内部可以有三种情况:

  • 返回True,表示当前限流类允许访问,继续执行后续的限流类。
  • 返回False,表示当前限流类不允许访问,继续执行后续的限流类。所有的限流类执行完毕后,读取所有不允许的限流,并计算还需等待的时间。
  • 抛出异常,表示当前限流类不允许访问,后续限流类不再执行。

1.3 全局配置限流类

  • 如果每个类都需要使用限流组件,可以在settings配置文件里配置全局限流
  • 配置完全局限流组件后,视图类中则不需要在单独引用限流组件
  • 如果说在全局配置中配置了限流频率,在限流了中给予无需再设置了
REST_FRAMEWORK = {"DEFAULT_THROTTLE_CLASSES":["app名称.限流组件的py文件.限流组件类名", ],"DEFAULT_THROTTLE_RATES": {# 限流频率"user": "10/m","xx":"100/h"}
}
  • 如果说某个视图类想单独使用某个限流组件
  • 在视图类下单独声明一个空的throttle_classes则可以覆盖全局默认的限流组件
class OrderView(APIView):throttle_classes = []def get(self, request):return Response({"code": 0, "data": "数据..."})

1.4 限流类源码执行流程

2. 序列化器 Serializer,转json序列化,转字典反序列化

2.1 序列化器作用

  • 序列化,序列化器会把模型对象转换成字典,经过视图中response对象以后变成json字符串
  • 反序列化,视图中request会把客户端发送过来的数据转换成字典,序列化器可以把字典转成模型
  • 反序列化,把客户端发送过来的数据进行校验,并存储入库

2.2 序列化类的使用

# 使用步骤:# 1 写个py文件,叫serializer.py# 2 写个类,继承serializers.Serializer# 3 在类中写要序列化的字段class PublishSerializer(serializers.Serializer):# 写字段,要序列化的字段name = serializers.CharField()addr = serializers.CharField()id = serializers.IntegerField()# 4 在视图类中使用,完成  序列化-多条-ser = PublishSerializer(instance=publish_list, many=True)  	-ser.data  序列化后的数据-单条:-ser = PublishSerializer(instance=publish)  	-ser.data  序列化后的数据

2.3 序列化类的快速使用

  • 视图类中引用
from app01.Serializer import PublisherSerializerclass PublishView(APIView):def get(self, request):publish_list = Publish.objects.all()ser = PublishSerializer(instance=publish_list, many=True)  # 如果序列化多条,要many=Truereturn Response({'code': 100, 'msg': '查询所有成功', 'results': ser.data})class PublishDetailView(APIView):def get(self, request, pk):publish = Publish.objects.filter(pk=pk).first()ser = PublishSerializer(instance=publish)  # 单个不写many=Truereturn Response({'code': 100, 'msg': '查询单条成功', 'results': ser.data})
  • 创建序列化类
from rest_framework import serializers
class PublishSerializer(serializers.Serializer):# 写字段,要序列化的字段name = serializers.CharField()addr = serializers.CharField()
  • 路由
urlpatterns = [path('publish/', views.PublishView.as_view()),path('publish/<int:pk>',views.PublishDetailView.as_view()),
]

2.4 序列化类反序列化校验

# 序列化类可以做字段校验---》三层
# 第一层:字段自己
serializers.CharField(max_length=12,min_length=3)# 第二层:局部钩子def validate_name(self, name):# 待校验的前端传入的name的数据if name.startswith("sb"):# 不行,抛异常raise ValidationError('不能以sb开头')return name# 全局钩子--》attrs前端多传的,这里不会有,attrs所有的参数def validate(self, attrs):print(attrs)# 多个字段同时校验# 出版社名和地址不能一样---》出版社前3个字不能和地址前3个字一样if attrs.get('name')[:3] == attrs.get('addr')[:3]:raise ValidationError('出版社名和地址不能一样')return attrs# 使用步骤# 写序列化类:写三层规则# 视图类中:ser = PublishSerializer(data=request.data)  # 把待校验数据传入if ser.is_valid():  # 做数据校验---》三层print(ser.data)else:print(ser.errors)  # 没有校验通过,打印错误信息

2.5 序列化类保存

# 使用步骤:# 1.在序列化类中,必须重写 create,完成真正的保存# 保存,必须重写createdef create(self, validated_data):# validated_data 校验过后的数据---》多传的数据,在这没有publish = Publish.objects.create(**validated_data)return publish  # 不要忘了返回新增的对象---》后续会拿着这个对象做序列化  ser.data--->根据它做序列化的# 2.在视图类中,数据校验通过后,调用ser.save()ser.save()  # 使用序列化类保存--》会报错---》咱们没有指定保存到那个表--》必须重写create方法# 修改功能,也要校验和保存# 修改使用步骤# 1.在序列化类中,必须重写 update,完成真正的修改def update(self, instance, validated_data): # 笨方法instance.name=validated_data.get('name')instance.addr=validated_data.get('addr')instance.save() # publish 对象的save---》保存到数据中# 高级方法# for key in validated_data:# setattr(instance,key,validated_data[key])# instance.save()return instance# 2.视图类中ser = PublishSerializer(instance=publish, data=request.data)if ser.is_valid():# ser.save()虽然新增或修改都是调用save,但是内部做了判断return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})else:return Response({'code': 101, 'msg': ser.errors}) 

2.6 序列化类的常用字段

  • 除了CharField 以外,还要很多别的---》表模型中 models.CharField --->基本一一对应
  • 如果跟 表模型中对不上:你统一用 CharField
序列化器
字段
模型
字段
序列化器字段选项
BooleanField BooleanField BooleanField()
CharField CharField
TextField等
CharField(
max_length=最大长度, min_length=最小长度,
allow_blank=False, 表示是否允许客户端提交空字符串,False表示不允许
trim_whitespace=True,表示是否移除字符串两边的空白字符,True表示移除
)
EmailField EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField CharField RegexField(regex=正则表达式, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField SlugField(max_length=50, min_length=None, allow_blank=False)
正则字段,验证正则模式 [a-zA-Z0-9*-]+
URLField URLField URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField UUIDField UUIDField(format='hex_verbose')
format: 设置UUID格式,一般默认使用hex_verbose
'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
'hex'"5ce0e9a55ffa654bcee01238041fb31a"
'int' - 如: "123456789012312313134124512351145145114"
'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressField IPAddressField IPAddressField(protocol='both', unpack_ipv4=False, **options)
IntegerField SmallIntegerFiled
IntegerField
BigIntegerField
IntegerField(max_value=最大值, min_value=最小值)
FloatField FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField DecimalField(
max_digits=数值的数字总个数, decimal_places=小数位个数, coerce_to_string=None, max_value=None, min_value=None)
DateTimeField DateTimeField DateTimeField(
format=api_settings.DATETIME_FORMAT, 表示日期格式
input_formats=None)
DateField DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField DurationField()
ChoiceField 对应整型或字符串中的choices=属性选项 ChoiceField(choices=元祖选项) choices与Django的用法相同
MultipleChoiceField 对应整型或字符串中的choices=属性选项 MultipleChoiceField(choices=元祖选项) choices与Django的用法相同
FileField FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField python里面的List ListField(child=模型列表, min_length=None, max_length=None)
DictField python里面的Dict DictField(child=模型对象)

字段的选项参数:

参数名称 作用
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空字符串
trim_whitespace 是否移除两边的空白字符
max_value 最小数值
min_value 最大数值

字段的通用选项参数:

参数名称 说明
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
miss 序列化时使用的默认值
allow_null 表明反序列化时该字段是否允许传入None,默认False
validators 表明反序列化时该字段使用的验证器函数
error_messages 表明反序列化时如果验证出错了,返回错误错误信息的字典
label 用于HTML展示API页面时,显示的字段名称。
如果不写,则默认采用模型的verbose_name,但是前提是当前序列化器继承ModelSerializer
help_text 用于HTML展示API页面时,显示的字段帮助提示信息
如果不写,则默认采用模型的help_text,但是前提是当前序列化器继承ModelSerializer

2.7 多表关联序列化和反序列化

2.7.1 定制返回格式之source

# 1.source 定制返回字段名,必须跟models表中字段必须对应,
book_name = serializers.CharField(source='name')
# 2.可以跨表查询
publish_name=serializers.CharField(source='publish.name')
# 3.所有字段都可以被转成CharField
2.7.2 定制返回字段
(1)方案一在模型表中定义方法
  • Models.py
class Book(models.Model):name = models.CharField(max_length=32)price = models.DecimalField(max_digits=5, decimal_places=2)publish_date = models.DateField(auto_now=True, null=True)publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)authors = models.ManyToManyField(to='Author')# 出版社详情def publish_detail(self):return {'name': self.publish.name, 'city': self.publish.city}# 作者详情def author_detail(self):author_list = []for author in self.authors.all():author_list.append({'name': author.name, 'age': author.age})return author_listdef __str__(self):return self.name
  • serializer
class BookSerializer(serializers.Serializer):name = serializers.CharField()price = serializers.IntegerField()publish_date = serializers.DateField(required=False)# publish_detail 此字段表中没有,通过在models表模式中定义方法# 此处直接应用,字段名必须与models模型表中的方法名一致publish_detail = serializers.DictField()# 可以搭配source使用authors = serializers.ListField(source='author_detail')
  • 展示前端

(2)方案二通过SerializerMethodField
  • 一定要配合一个方法--》get_字段名,方法返回什么,前端就看到什么

  • Serializer.py

class BookSerializer(serializers.Serializer):name = serializers.CharField()price = serializers.IntegerField()publish_date = serializers.DateField(required=False)publish_detail = serializers.SerializerMethodField()def get_publish_detail(self, obj):return {'name': obj.publish.name, 'city': obj.publish.city}authors = serializers.SerializerMethodField()def get_authors(self, obj):author_list = []for author in obj.authors.all():author_list.append({'name': author.name, 'age': author.age})return author_list
  • 前端展示

(3)方案三 子序列化
  • serializer.py
  • 自定义字段需要使用的表都序列化一下,就是创建class 表名Serializer(serializers.Serializer):
class PublishSerializer(serializers.Serializer):name = serializers.CharField()city = serializers.CharField()email = serializers.EmailField()class AuthorSerializer(serializers.Serializer):name = serializers.CharField()age = serializers.CharField()class BookSerializer(serializers.Serializer):name = serializers.CharField()price = serializers.DecimalField(max_digits=5, decimal_places=2)publish_date = serializers.DateField()# 子序列化# source='publish' 映射model表中的publish字段publish_detail = PublishSerializer(source='publish')# 多条结果时 many=Trueauthors_list = AuthorSerializer(source='authors', many=True)
  • 前端展示

2.8 多表关联反序列化和反序列化保存

# 反序列化保存
# 使用同一个序列化类会出现-序列化字段和反序列化字段不一致-序列化字段namepricepublish_detailauthor_list-反序列化字段:namepricepublishauthor-如果是共同的,不需要额外处理-如果是不同的,需要通过字段参数控制read_only	表明该字段仅用于序列化输出,默认False,序列化过程write_only	表明该字段仅用于反序列化输入,默认False,反序列化过程# 注意:1 read_only write_only 控制序列化类中某些字段,只用来序列化或反序列化2 重写updata和create,保存逻辑,我们自己写3 视图类中 serializer = BookSerializer(instance=pk, data=request.data)-后续在序列化类中的update中def update(self, instance, validated_data):-instance就是当时给的# 不要有误区---》增加图书,只是增加图书,选择作者和出版社(传:id)
{name:书名,price:11,publish:{'name':'北京出版社',city:北京},authors:[{},{}]}{name:书名,price:11,publish:2,authors:[1,2]}
  • views.py
class BookView(APIView):def post(self, request):serializer = BookSerializer(data=request.data)if serializer.is_valid():print(serializer.validated_data)  # 校验过后的数据serializer.save()return Response('成功')else:return Response({'code': 100, 'msg': serializer.errors})class BookDetailView(APIView):def put(self, request, pk):# obj = Book.objects.all().filter(pk=pk).first()# 传入的instance是什么,到了 update中,instance就是什么serializer = BookSerializer(instance=pk, data=request.data)if serializer.is_valid():serializer.save()return Response('成功')else:return Response({'code': 100, 'msg': serializer.errors})
  • serializer.py
class BookSerializer(serializers.Serializer):name = serializers.CharField()price = serializers.DecimalField(max_digits=5, decimal_places=2)publish_date = serializers.DateField(required=False,default=datetime.now)#### 序列化的字段和反序列化字段不一致###### 1 笨办法:再写个序列化类,单独用来反序列化## 2 通过 read_only write_only	控制# 这个字段用来做序列化publish_detail = PublishSerializer(source='publish',read_only=True)# 这个字段用来做序列化authors_list = AuthorSerializer(source='authors', many=True,read_only=True)# 反序列化字段--》只用来保存---》多表关联publish=serializers.IntegerField(write_only=True) # 前端传入数字---》自动跟出版社id做对应authors=serializers.ListField(write_only=True)# 反序列化字段-->可以随意命名,跟表字段没关系--》但是后续保存和修改要对应好才行# publish_id=serializers.IntegerField(write_only=True) # 前端传入数字---》自动跟出版社id做对应# authors_xx=serializers.ListField(write_only=True)# 保存def create(self, validated_data):publish_id=validated_data.pop('publish')authors=validated_data.pop('authors')book=Book.objects.create(**validated_data,publish_id=publish_id)book.authors.add(*authors) # 向中间表中插入数据return book# 更新def update(self, instance, validated_data):publish_id=validated_data.pop('publish')authors=validated_data.pop('authors')book_qs=Book.objects.filter(pk=instance) # 查询qs对象book_qs.update(**validated_data,publish_id=publish_id) # 使用qs更新instance=book_qs.first() # 单个对象instance.authors.set(authors) # 向中间表中插入数据# instance.author.clear()# instance.authors.add(*authors)return instance

2.9 反序列化校验源码分析

#1  执行 ser.is_valid() 就会执行 反序列化的校验---》字段自己--》局部钩子---》全局钩子
#2  入口是:ser.is_valid()---》BaseSerializer 找到了1 自己写的BookSerializer---》serializer.Serializer---->BaseSerializer 2 源码如下def is_valid(self, *, raise_exception=False):# self 是 ser对象---》自己写的BookSerializer的对象--》一开始没有# 一旦有了,就不执行了,优化is_valid被多次调用,只会走一次校验if not hasattr(self, '_validated_data'):try:# 一旦执行过,以后self中就有_validated_data# 接下来看self.run_validation(self.initial_data)self._validated_data = self.run_validation(self.initial_data)except ValidationError as exc:self._validated_data = {}self._errors = exc.detailelse:self._errors = {}if self._errors and raise_exception:raise ValidationError(self.errors)return not bool(self._errors)3 self.run_validation(self.initial_data)---》serializer.Serializer类的,不要按住ctrl点击,否则会进 Field 类,看错了4 serializer.Serializer类的run_validationdef run_validation(self, data=empty):# data前端传入的--{"name":"张三","age":68}# value是---》前端传入的,字段自己校验通过的字典---{"name":"张三","age":68}value = self.to_internal_value(data) # 执行局部钩子try:self.run_validators(value) # 先不看,忽略掉# self 是 BookSerializer的对象,如果我们写了全局钩子,走我们自己的,如果没写,走父类的,父类的根本没做校验# value={"name":"张三","age":68}value = self.validate(value)# 执行全局钩子except (ValidationError, DjangoValidationError) as exc:raise ValidationError(detail=as_serializer_error(exc))return value5 全局钩子读完了:self 是 BookSerializer的对象,如果我们写了全局钩子,走我们自己的,如果没写,走父类的,父类的根本没做校验6 局部钩子:value = self.to_internal_value(data)--》Serializer类的# for循环着去BookSerializer的对象中反射  validate_字段名的方法,如果有就执行,没有就不执行def to_internal_value(self, data):for field in fields: # 序列化类中所有字段类的对象 name=CharField()# self 是BookSerializer类的对象# 去BookSerializer类中,反射  validate_field字段类的对象.field_namevalidate_method = getattr(self, 'validate_' + field.field_name, None)try:# 如果能拿到,说明咱么写了局部钩子if validate_method is not None:# 执行局部钩子--》传入了当前字段的value值validated_value = validate_method(validated_value)except ValidationError as exc:# 如果抛异常,会被捕获errors[field.field_name] = exc.detailexcept DjangoValidationError as exc:errors[field.field_name] = get_error_detail(exc)except SkipField:passelse:set_value(ret, field.source_attrs, validated_value)if errors:raise ValidationError(errors)return ret# #####读了局部和全局钩子的执行位置#####
# 保存,修改也好,都要用validated_data,它是最准确的

2.10 ModelSerializer使用

# 之前写序列化类,没有显示指明跟哪个表一一对应# ModelSerializer 可以跟表做一一对应关系-序列化类中,就不需要一个个写字段了--》跟表有对应关系-序列化类中,就不需要重写create和update# 局部钩子全局钩子跟之前一样(注意层级)
  • serializer.py
from .models import Book
class BookSerializer(serializers.ModelSerializer):class Meta:model=Book# all代表全部字段# fields = '__all__'# 部分字段放在列表里fields=['id','name','publish_detail','authors_list']extra_kwargs={'name':{'max_length':8}, # 限制name不能超过8'publish':{'write_only':True},'authors':{'write_only':True},}# 自己再重写的字段# 这个字段用来做序列化publish_detail = PublishSerializer(source='publish',read_only=True)# 这个字段用来做序列化authors_list = AuthorSerializer(source='authors', many=True,read_only=True)# 不需要写create了,但是字段必须是:publish和authors
  • 前端传参数图片

3. 断言

# 断言 语法
assert 条件,'字符串'
翻译成if
if 条件:条件成立,继续走后续代码
else:raise Exception('字符串')a = 10## assert 后写条件,只要不符合条件,就会抛AssertionError异常,后面写异常信息
assert a == 11, ("不等于11,报错了")# 等同于---》上面只要一行代码,源码中喜欢用
if not a == 11:raise Exception('不等于11,报错了')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/31881.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

04认证和权限

认证和权限 一、认证 在开发后端的API时,不同的功能会有不同的限制,例如:无需认证,就可以访问并获取数据。 需认证,用户需先登录,后续发送请求需携带登录时发放的凭证在drf中也给我们提供了 认证组件 ,帮助我们快速实现认证相关的功能 【1】认证组件使用步骤 (1)创建认…

AI agent的几种实现思路

February 24, 2024 提示工程学习笔记(二)在 上一篇笔记 中,我们学习了很多提示工程相关的技术,比如思维链(CoT)和最小到最多提示(Least-to-Most Prompting)等,显著改善了大模型的推理能力。尽管如此,我们常常还是会看到这样的现象:大模型可以准确地生成解决问题的逻…

振弦采集仪在岩土工程监测中的重要性及应用案例分享

振弦采集仪在岩土工程监测中的重要性及应用案例分享 岩土工程监测是为了确保土地和建筑物的稳定性以及确保施工安全而进行的一项重要工作。河北稳控科技振弦采集仪是岩土工程监测中一种常用的仪器设备,通过测量土体振动频率来评估土体的稳定性和强度变化,具有重要的监测功能。…

如何设计实用的ITSM自助服务台

在现代IT服务管理(ITSM)领域中,自助服务台已成为IT运维环境的核心组件。它作为企业内部信息中心与其他部门用户之间的桥梁,一个以用户为中心的平台,更注重用户的自主性和自助能力,使用户能够直接访问所需的IT信息、工具和服务,解决他们在日常工作中遇到的各种IT问题和需…

树做题笔记

\(\color{#3498D8}(1)\) P4281 [AHOI2008] 紧急集合 / 聚会给定一棵 \(n\) 个节点的树。\(m\) 次询问,每次给定 \(a, b, c\),求一个节点 \(u\) 并使得三个点到 \(u\) 的距离和最小。求 \(u\) 和最小距离和。 \(n, m \le 5 \times 10^5\)。三个点 \(a, b, c\) 在树上的位置关系…

如何将pdf文件转换成jpg图片?这3种方法超简单

日常我们有时把会图片文件转化为PDF文件,但是有时候由于工作的需要,在对文本、图片进行处理时,可能会将PDF文件转化为图片格式文件,比如pdf怎么转换成jpg图片等。那遇到pdf转换成图片的情况该如何转换?分享3个超简单的方法。 方法一、截图转换 如果你的PDF文件内容不会太过…

shell函数和数组

函数 定义函数##第一种:简单常用 函数名(){ 脚本(命令集合) }##第二种: function 函数名{ 脚本内容(命令集合) }##第三种 function 函数名(){ 脚本(命令集合) }函数使用#!/bin/bash num(){ ##num是定义的函数名称 a=66 echo ${a} }num ##这里的num是调用上面的num函…

BOSHIDA AC/DC电源模块的故障诊断与维修技巧

BOSHIDA AC/DC电源模块的故障诊断与维修技巧 AC/DC电源模块是一种常用的电力转换设备,用于将交流电转换为直流电供给电子设备。然而,由于使用环境和操作不当等原因,电源模块可能会出现故障。本文将介绍AC/DC电源模块的故障诊断与维修技巧。 故障诊断的第一步是检查输入电源…