zoukankan      html  css  js  c++  java
  • 15.DRF学习以及相关源码阅读

    1.http请求协议

    代码很枯燥,结果和奇妙。

    1.cbv
    	django.vuews import View
        classs LoginView(View):
            def get(self,requset):
                pass
    2.classmethod $ classonlymethod
    3.getattr hasattr setattr
    4.self定位
    	- 始终代表调用者本身
    5.http请求协议:就是约定成俗的规范,解析数据的规范
    6.form enctype 
    	解析参数的格式
    7.  javascript object {name: "alex"} ===>json转换的方式
    	7.1 data:Json.stringify({name: "alex", age: 18}) ==>json.dumps(data)
            JSON.parse(data) ===> json.loads()
        
    

    今日详情:

    1. djaong-restframework

    ​ 1.1 ApiVIew

    urlpatterns = [
        re_path('^course/$', views.CourseView.as_view()),
    ]
    url_mapping = {
        "login": "LoginView"
    }
    第一步:从as_view向里看
    rest-framework如何对django的view进行扩展的呢
    

    ​ 1.2 解析器组件

    ​ 1.3 序列化组件

    ​ 1.4 认证组件

    ​ 1.5 权限组件

    ​ 1.6 频率组件

    ​ 1.7 url控制器组件

    ​ 1.8 分页组件

    ​ 1.9 响应器组件

    今日内容总结:

    • 知识点复习总结

      • 什么是编程

        数据结构+算法

      • 什么是rest

        https://www.jd.com #通过url拿到数据
        2.1原始url
            127.0.0.1:8001/books/id/    
            127.0.0.1:8001/books/?name=""
            127.0.0.1:8001/books/delet/id/      
            这种url方式使得接口看起来多而且杂,维护不方便    
        url用来唯一定位资源,用http请求方式来区分用户对数据的操作方式	
        2.2rest url
        	1.url设置规范
                GET:127.0.0.1:8001/books/    # 获取所有数据    
                GET:127.0.0.1:8001/books/id/ # 获取单挑数据
                POST:127.0.0.1:8001/books/   # 增加数据   
                DELETE:127.0.0.1:8001/books/id/ # 删除数据
                UPDATE:127.0.0.1:8001/books/id/ # 更改数据     
            2.数据响应规范
            	GET:127.0.0.1:8001/books/    # 获取所有数据    返回[{},	 ,]
                GET:127.0.0.1:8001/books/id/ # 获取单挑数据	 返回{}单条数据
                POST:127.0.0.1:8001/books/   # 增加数据       返回{}添加成功
                DELETE:127.0.0.1:8001/books/id/ # 删除数据    返回‘’返回空
                put/UPDATE:127.0.0.1:8001/books/id/ # 更改数据    返回更新后的完整数据
            3.错误处理
            	{"error":"message"}
        
      • cbv

      • classmethod & classonly method

      • getattr hasattr setattr

        动态对对象的属性进行操作

      • self定位

        始终定位的是自己

      • http请求数据的协议

        后端用什么方式来解析请求的数据

      • from表单的enctype里面的三种请求协议

        urlencoded, multi-formdata, text/plain

      • JavaScript object 序列化成json

        object =>json(json.stringify) json==>object(JSON.parse())




    class LoginView(ApiView):
    	def post(self,request):
            data = request.data  #新的request对象
    		return
    
    class ApiVIew(View):
    
    	@classmethod
    	def as_view(self, request):
    		pass 
    		super(ApiView,self).as_view(**initkwargs)
    	def dispatch():
    		request = self.initialize_request(request, *args, **kwargs)
    		self.request = request
    parser_context = self.get_parser_context(request)
    
    return Request( 
        request,  # 原生reqeust
        parsers=self.get_parsers(),  # get_parsers()
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )
    
    ApiView.py中的
    def get_parsers(self):
        """
            Instantiates and returns the list of parsers that this view can use.
            """
        return [parser() for parser in self.parser_classes]
    from rest_framework.request import Request
    data = request.data  #新的request对象
    从reqeust中.出来的,所以其Request中找这个属性
    

    3.解析器源码流程:

    __getattr__在属性未找到的时候
    第一步:执行as_view()
    第二步:自己写的里面没有as_view()函数去父类中找
    第三部:执行view中的as_view()方法,返回view函数
    第四步:url和驶入函数之间的绑定关系建立完毕{“login”: view}等待用户请求
    第五步:接收到用户请求:login,到建立好的关系里面执行对应的视图函数:view(request)
    第六步:视图函数的执行结果是什么就返回给客户,self.dispatch(),self.dispatch()的执行结果
    第七步:此时的self代表的是LoginView的实例对象
    第八步:开始找dispatch方法,self里面没有,LoginVIew里面也咩有,在ApiView中
    第九步:开始执行apiview中的dispatch方法
    第十步:在最后找到http方法(get,post,put,delete),根据请求的类型查找(reqeust.method.lower())
    第十一步:执行第十步找到的方法,开始执行找到的方法self.get()代表的是LoginView的实例化对象
    	11.1 假设接受到的时候POST请求,执行reuqest。data
    	11.2 分析结果,所有的解析工作都是在request。data里面实现的,且data是一个方法(被属性化之后的)
    	11.3 拿取data
    		 @property
             def data(self):
             	if not _hasattr(self, '_full_data'):
             		self._load_data_and_files()
             	return self._full_data
    	11.4 执行_load_data_and_files()
    	11.5 parser = self.negotiator.select_parser(self, self.parsers)
    		11.5.1parsed = parser.parse(stream, media_type, self.parser_context)
            11.5.2 self.get_parses()
            	 return [parser() for parser in self.parser_classes]
            11.5.3 parser_classes = api_settings.DEFAULT_PARSER_CLASSES
            11.5.4 api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
            11.5.6 APISettings是一个类
            11.5.7 找不到parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    			的时候就去__getattr__里面找
            11.5.8 __getattr__里面有这么一句话 val = self.user_settings[attr]
            	@property
                def user_settings(self):
                    if not hasattr(self, '_user_settings'):
                        self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
                    return self._user_settings
            11.5.9 首先找自己的settiongs,之后找rest_framework的settings
            
    	11.6 得到self._data = parsed.data
    	11.7 DRF将self.date = self._data
    	11.8 reqeust.data
    第十二部:在loginView里面找到对应的方法,执行该方法,最后返回给用户
    
    • DRF的所有功能都是在as_view()和dispatch()里面重写的
    • 而解析器组件在dispatch方法里面重写了,具体实在重新封装的Request对象中
    class ParseJson():
    
    	pass
    class ParseForm():
        data = request.body.decode('utf-8')
        data.split("&")
        return data
    def select_parse(request,ParseJson, ParseForm):
        return 
    
    

    知识点回顾:

    1.cbv

    2.classmethod & classonlymethod

    from django.utils.decorators import classonlymethod
    class JinJa(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        @classmethod
        def sleeping(cls):
            print("sleeping")
    
        @classonlymethod
        def shui(cls):
            print("shui_jiao")
    装饰器在类加载的时候执行。        
    

    3.getattr,hasattr,setattr

    4.self定位

    谁调用就是谁。
    

    5.http请求协议

    6.form enctype

    7.JavaScriot object {name: "pizza"} =====>json转换

    # 可以建立一个apps文件夹,将每个app放入其中
    APPS_DIR= os.path.join(BASE_DIR, "apps")
    sys.path.append(APPS_DIR)
    

    2.view的请求流程

        1.通过as_view
            def view(request, *args, **kwargs):
                # 实例化一个对象,对象名称为self,self是cls的对象,谁调用cls
                # cls就是谁(当前调用cls的是BookVIew)
                # 所以,此时的self就是BookView的实例化对象
                self = cls(**initkwargs) 
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
                self.setup(request, *args, **kwargs)
        	返回函数 return self.dispatch(request, *args, **kwargs)
        2.执行dispatch方法
        def dispatch(self, request, *args, **kwargs):
            """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
            self.args = args
            self.kwargs = kwargs
            # 此时的request对象指向原始的request对象
            # 给self这个实例化对象赋值:原始的request(下面这行代码对request进行加工)
            request = self.initialize_request(request, *args, **kwargs)
            # 重新对request进行赋值
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
            try:
                self.initial(request, *args, **kwargs)
                # 通过请求的方式进行路由匹配,如果早安全的方式里就尝试获取执行的函数
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    # 获取不到就报错
                    handler = self.http_method_not_allowed
                response = handler(request, *args, **kwargs)
            except Exception as exc:
                # 通过异常处理,对异常结果进行返回
                response = self.handle_exception(exc)
            # 对返回结果进行处理
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
    

    4.rest使用方式

    1.from rest_framework.views import APIVIew
    2.继承VPIView
    	class HomeView(APIVIew):
            def get(self):
                pass
    3.url.py
        from django.urls import path,include,re_path
        urlpatterns = [
            re_path('^course/$', views.CourseView.as_view()),
        ]
    4.def post(self,request):
        origin_data = request.data
        ......
        return HttpResonse({})
    源码剖析:request.data是从哪里来的?
    	1.dispatch方式中对request进行重新构造
        	request = self.initialize_request(request, *args, **kwargs)
            
            def initialize_request(self, request, *args, **kwargs):
                parser_context = self.get_parser_context(request)
                return Request(
                    request,
                    parsers=self.get_parsers(),
                )
     		# 1.1自己里面没有定义
            def __init__(self, request, parsers=None, authenticators=None,
                         negotiator=None, parser_context=None):
                self._request = request
                self.parsers = parsers or ()   # self.get_parsers()的结构
            # 1.2还是在Requet里面找
            @property
            def data(self):
                if not _hasattr(self, '_full_data'):
                    self._load_data_and_files()
                return self._full_data
            # 1.3_load_data_and_files()
            def _load_data_and_files(self):
                if not _hasattr(self, '_data'):
                    self._data, self._files = self._parse()  # 执行执行这个方法
                    if self._files:
                        self._full_data = self._data.copy()
                        self._full_data.update(self._files)
                    else:
                    if is_form_media_type(self.content_type):
                        self._request._post = self.POST
                        self._request._files = self.FILES
    		# 1.4 self._parse()
            def _parse(self):
                parser = self.negotiator.select_parser(self, self.parsers) # 解析器对象
                parsed = parser.parse(stream, media_type, self.parser_context) # 解析之后的结果
                return (parsed.data, parsed.files)
            # 1.5  self.parsers  # 获取解析器
    		def get_parsers(self): 
            	return [parser() for parser in self.parser_classes]
            # 1.6   self.parser_classes 一个属性
            class APIView(View):
                parser_classes = api_settings.DEFAULT_PARSER_CLASSES
                
           	api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
            
            # 因为找不到所以执行__getattr__方法
            def __getattr__(self, attr):
                try:               
                    val = self.user_settings[attr] # 我们项目的settings文件中的默认参数
                except KeyError:
                	# 没找到就去找rest的settings获取默认的参数
                    val = self.defaults[attr]
    
                # Coerce import strings into classes
                if attr in self.import_strings:
                    val = perform_import(val, attr) # 这里的到的解析器类对象列表
    
                # Cache the result
                self._cached_attrs.add(attr)
                setattr(self, attr, val)
                return val  # 这里返回的就是解析器列表
            # 1.7 perform_import(val, "DEFAULT_PARSER_CLASSES") 
            # val是这个:
            	 'DEFAULT_PARSER_CLASSES': [
                        'rest_framework.parsers.JSONParser',
                        'rest_framework.parsers.FormParser',
                        'rest_framework.parsers.MultiPartParser'
                    ],
           	def perform_import(val, setting_name)
                if val is None:
                    return None
                elif isinstance(val, str):
                    return import_from_string(val, setting_name)
                elif isinstance(val, (list, tuple)):
                    return [import_from_string(item, setting_name) for item in val]
                return val
            # 1.8 
            def import_from_string(dotted_path):
                try:
                    module_path, class_name = dotted_path.rsplit('.', 1)
                except ValueError as err:
                    raise ImportError("%s doesn't look like a module path" % dotted_path) from err
    
                module = import_module(module_path)  # 动态导包进行包的获取
    
                try:
                    return getattr(module, class_name)
    

    super方法的解释
    	1.不使用super我们可以通过以下方式
        class JinJa:
            def __init__(self):
                print("jinja is a parse")
        class To:
            def __init__(self):
                To.__init__(self,)
        2. 使用super方式
        	2.1 python2
            class JinJa:
                def __init__(self):
                    print("jinja is a parse")
            class To:
                def __init__(self):
                    super(TO,self).__init__(*args, **kwargs)# super(参数必须传递,self)有参数必须传递参数
            2.2 python3
            class JinJa:
                def __init__(self):
                    print("jinja is a parse")
            class To:
                def __init__(self):
                    super().__init__(*args, **kwargs)# 有参数必须传递参数
    

    *动态导包

    import importlib
    moudle = input("请输入要导入的模块:")
    moudle = importlib.import_module(module)
    # 通过字符串方式引入模块
    

    知识点回顾:

    1.三元运算:“alex” if a else "wusir"

    2.列表推导式

    3.getattr

    4.djaong settiong文件查找顺序global_settings

    5.动态import

    6.Django原生的

    5.序列化器

    # django原生的serializer
    from django.core.serializers import serialize
    class CourseView(APIView):
        # 这是局部的解析器,自定制的地方
        parser_classes = [FormParser, JSONParser]
            def get(self, request):
            course_list = list()
            cour = Course.objects.all()
            for course in cour:
                course_dict = {
                    "course_name": course.course_name,
                    "description": course.description,
                }
                course_list.append(course_dict)
            ser_data = serialize("json", cour)
            print(ser_data)
            # ensure_ascii显示中文
            return HttpResponse(json.dumps(course_list, ensure_ascii=False))
    
    
    # 1.开始使用序列化类
    	- 导入模块
        - 建立一个序列化类
        - 获取queryset
        - 开始序列化
        - 获取序列化后的数据,返回给客户端
    class BookSerializer(serializers.Serializer):
        # nid = serializers.IntegerField()
        title = serializers.CharField(max_length=32)
        price = serializers.DecimalField(max_digits=10, decimal_places=2)
        # 可以使用source字段覆盖当前的字段的显示,read_only只读字段
        publish = serializers.CharField(max_length=32)
        publish_name = serializers.CharField(read_only=True, max_length=32, source="publish.name")
        city_name = serializers.CharField(read_only=True, max_length=32, source="publish.city")
        # authors = serializers.CharField(max_length=32)
        authors = serializers.SerializerMethodField()
    
        # instance是这次循环的书籍对象(额外字段都可以)
        def get_authors(self, instance):
            author_list = []
            for author in instance.authors.all():
                author_list.append(author.name)
            return author_list
    
        # 如果用的是Serializer,这里需要你自己写create方法
        def create(self, validated_data):
            validated_data["publish_id"] = validated_data.pop('publish')
            book_obj = Book.objects.create(**validated_data)
            return book_obj
    
    
    class BookView(APIView):
        def get(self, request):
            # 获取queryset
            origin_data = Book.objects.all()
            # 将获取到的数据传入序列化类,数据多条是many=True
            ser_data = BookSerializer(origin_data, many=True)
            return Response(ser_data.data)
    
        def post(self, request):
            client_data = request.data
            ser_data = BookSerializer(data=client_data)
            if ser_data.is_valid():
                book = ser_data.save()
                authors = Author.objects.filter(nid__in=request.data["authors"])
                book.authors.add(*authors)
                return Response(ser_data.data)
            else:
                return Response(ser_data.errors)
    
    # 2.序列化器源码流程
    	- 1 每次传入的数据都是通过is_valid验证的
            def is_valid(self, raise_exception=False):
            # 其实就是判断数据有没有经过验证,但是第一次的数据,肯定没有验证
            if not hasattr(self, '_validated_data'):
                try:
                    # 所以执行run_validation,  self.initial_data = data  传进来的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)
        - 2 执行正真的验证数据的方法
            def run_validation(self, data=empty):
             # validate_empty_values是为了检验有一些值不需要传入的但是你却传入了,就是对于不需要进行验证值得一次过滤,比如read_only的字段就过滤掉了
            (is_empty_value, data) = self.validate_empty_values(data)
            if is_empty_value:
                return data
            value = self.to_internal_value(data)
            # 这个是执行我们在serialzier类中定义的validate方法,你需要的逻辑可以在其中对于数据进行检验
            value = self.validate(value)
            self.run_validators(value)
            return value
        - 3 关键还是to_internal_value方法但是我读不进去了,
            def to_internal_value(self, data):
            ret = OrderedDict()
            errors = OrderedDict()
            # 拿到需要写入的字段,其中使用元类赋值的
            fields = self._writable_fields
    
            for field in fields:
            value = self.validate(value)
                # 钩子函数方法,可以自定义对联合的字段进行验证,单字段检验,多字段需要使用validate方法
                validate_method = getattr(self, 'validate_' + field.field_name, None)
                primitive_value = field.get_value(data)
                try:
                    # 这里执行的每个字段自己的run_validation方法,其中还执行了自己的to_internal_value方法,我的意思是字段Field自己的而不是serialziers类自己的。
                    validated_value = field.run_validation(primitive_value)
                    if validate_method is not None:
                        validated_value = validate_method(validated_value)
                # 都没有问题的时候执行else
               	else:
                    set_value(ret, field.source_attrs, validated_value)
         - run_validators这个方法其实就是我们自己写的验证器(用两种在序列化类执行的时候是Meta中的,而每个字段执行的时候是我们在每个字段中写的验证呢个列表)
        	def run_validators(self, value):
                errors = []
                for validator in self.validators:
                    if hasattr(validator, 'set_context'):
                        validator.set_context(self)
    
                    try:
                        # 由于这里并没有接收参数,所以我们的序列化可以没有返回,只需要抛出错误即可;同时你可以在序列化类中使用slef给调用的实例对象,设置参数(如果你希望验证过后得到一个值的时候)。
                        validator(value)
                    except ValidationError as exc:
                        # If the validation error contains a mapping of fields to
                        # errors then simply raise it immediately rather than
                        # attempting to accumulate a list of errors.
                        if isinstance(exc.detail, dict):
                            raise
                        errors.extend(exc.detail)
                    except DjangoValidationError as exc:
                        errors.extend(get_error_detail(exc))
                if errors:
                    raise ValidationError(errors)
        - 4 获取字段
    		fields = self._writable_fields
        	@property
            def _writable_fields(self):
                for field in self.fields.values():
                    if not field.read_only:
                        yield field
        - 5 关于slef.fields的来源这是非常关键的(找来找去找不到,在他继承的元类中找到了)
        这里有一篇博客可以自己去看下,之后接着看源码https://www.cnblogs.com/machao/p/8708584.html
        class SerializerMetaclass(type):
            @classmethod
            def _get_declared_fields(cls, bases, attrs):
                # 然后我们发现我们serializer中定义的那些字段他全部是类属性,然后就可以同属性字典拿出来
                fields = [(field_name, attrs.pop(field_name))
                          for field_name, obj in list(attrs.items())
                          if isinstance(obj, Field)]
                # 就是根据创建的计数器大小排序
                fields.sort(key=lambda x: x[1]._creation_counter)
                # 然后还发现了一个好东西,reversed用来他继承的多个类反转一下,涉及到了类的多继承中mro
                # 然后这里的作用就是将他继承的父类中的字段也加入到自己的fields字段中,其实就是serializer的嵌套时的字段处理过程,但是我写不出来啊,大佬还是大佬
                for base in reversed(bases):
                    if hasattr(base, '_declared_fields'):
                        fields = [
                            (field_name, obj) for field_name, obj
                            in base._declared_fields.items()
                            if field_name not in attrs
                        ] + fields
    
                return OrderedDict(fields)
    
            def __new__(cls, name, bases, attrs):
                # 元类是在继承的类被创建的时候执行的它的__new__方法,可以看到里面执行了一个类方法
                # 解释一下三个参数吧
                # name就是自己类的名称,
                # base就是除了继承的元类以外的其他继承类的一个元组
                # attr是自己类的属性字典(是类的不是实例的)
                attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
                return super().__new__(cls, name, bases, attrs)
          - 6 然后又执行了下面的代码
        	for field in fields: # 拿到每个字段,找到前面加validate的方法(自定义钩子函数对数据进行处理)
                validate_method = getattr(self, 'validate_' + field.field_name, None)
                
                primitive_value = field.get_value(data)
                try:
                    validated_value = field.run_validation(primitive_value)
                    if validate_method is not None:
                else:
                    set_value(ret, field.source_attrs, validated_value)
          - 7 field.get_value(data)注意这里开始执行的就是field的方法了不是self的,下面几个都是
        	# 是不是有点蒙,找不到了,然后突然发现CharField继承的是field,去它里面找,发现有
            # 其实这个时候的field是什么有没有思考过,其实他就是CharField(max_length=32)
            # 所以这里的这个get_valus是field中的,其实我第一遍找错了
        	def get_value(self, dictionary):
                if html.is_html_input(dictionary):
                    return html.parse_html_dict(dictionary, prefix=self.field_name) or empty
                # 拿出没有field_name对应的值返回
                return dictionary.get(self.field_name, empty)
          - 8 接下来执行了field.run_validation()方法
        	# CharField中有这个方法,但是它还是super了一下我们去看看
        	def run_validation(self, data=empty):
            if data == '' or (self.trim_whitespace and str(data).strip() == ''):
                if not self.allow_blank:
                    self.fail('blank')
                return ''
            return super().run_validation(data)
         - 9 field中的run_validation
            def run_validation(self, data=empty):
            (is_empty_value, data) = self.validate_empty_values(data)
            # 先去看第十步,如果is_empty_value为true就不检验
            if is_empty_value:
                return data
            # 这里执行的每个字段中的to_internal_value方法
            value = self.to_internal_value(data)
            self.run_validators(value)
            return value
        	# --------------CharField中的--------------------
        	def to_internal_value(self, data):
            CharField中的除了(str, int, float,)其他都报错
            if isinstance(data, bool) or not isinstance(data, (str, int, float,)):
                self.fail('invalid')
            value = str(data)
            return value.strip() if self.trim_whitespace else value
        	# 记下来继续执行self.run_validators(value)方法,发现CharField中没有,可以自己定义,去field中找
            def run_validators(self, value):
                errors = []
                # self.validator这个自己看在,field里面
                for validator in self.validators:
                    # 说明validator中有一个属性叫做set_context
                    if hasattr(validator, 'set_context'):
                        validator.set_context(self)
                    try:
                        # 这里执行各自的验证逻辑
                        validator(value)
            # 然后到这里基本就完了,还有一步,逻辑简单自己看吧
            set_value(ret, field.source_attrs, validated_value)
            # 所以字段都检验完了之后执行mate中的validtors方法,和字段中的类似,自己看一下
            
            self.run_validators(value)
            # 执行的是field中的run_validators,但是get_validators自己的serializer中有
            def get_validators(self):
                meta = getattr(self, 'Meta', None)
                validators = getattr(meta, 'validators', None)
                return list(validators) if validators else []
            # 检验没出错就返回值
            return(values)
        - 10 执行field中的validate_empty_values
        	# 其中包括四种返回方式,
            *引发“ValidationError”,表示数据无效。
            *引发“SkipField”,表示该字段应该被忽略。
            *返回(True, data),表示应该为空值返回,不应用任何进一步验证。
            *返回(False, data),表示一个非空值,应该正常应用验证。
        	if self.read_only:
                return (True, self.get_default())
            if data is empty:
                if getattr(self.root, 'partial', False):
                    raise SkipField()
                if self.required:
                    self.fail('required')
                return (True, self.get_default())
            if data is None:
                if not self.allow_null:
                    self.fail('null')
                elif self.source == '*':
                    return (False, None)
                return (True, None)
            return (False, data)
    

    注意:看序列化类的时候特别注意self,因为我看的有点模糊,第一次看的时候就没有分清。

    其实到这里有没有人疑惑为什么,serializers可以点出来各种的XXField字段,看了半天没看出问什么?然后我就继续找,属性/方法,突然全局搜索了一下出来了,他直接全部给导入了,我还以为他继承了,但是一想继承也不可能啊。牛皮了

    from rest_framework.fields import (  # NOQA # isort:skip
        BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField,
        DictField, DurationField, EmailField, Field, FileField, FilePathField, FloatField,
        HiddenField, HStoreField, IPAddressField, ImageField, IntegerField, JSONField,
        ListField, ModelField, MultipleChoiceField, NullBooleanField, ReadOnlyField,
        RegexField, SerializerMethodField, SlugField, TimeField, URLField, UUIDField,
    )
    from rest_framework.relations import (  # NOQA # isort:skip
        HyperlinkedIdentityField, HyperlinkedRelatedField, ManyRelatedField,
        PrimaryKeyRelatedField, RelatedField, SlugRelatedField, StringRelatedField,
    )
    

    知识点回顾:

    1.解析器组件
    	- 解析器组件是用来解析用户请求的数据的(application/json), content-type
        - 必须继承APIView
        - request.data触发解析
    2.序列化组件
    	2.1 Django自带的serializer
         	2.1.1 from django.serializer import serialize
            2.1.2 Origin_data = Book.object.all()
            2.1.3 serializer_data = serialzie("json", origin_data)
        2.2 DRF的序列组件
        	- get 接口设计
                2.2.1 from rest_framwork import serializers
                2.2.2 创建一个序列化类
                    class BookSerialzier(serializer.Serializer):
                        publish_name = serializer(read_obly=True, source="publish.name")
                        authors_list = serialzier.SerializerMethodField()
    
                        def get_authors_list(self, instance):
                            pass
                2.2.3 开始序列化
                    origin_data = Book.objects.all()
                    ser_data = BookSerializer(data=origin_data, many=true)
                    return Response(ser_data.data)
             - post接口设计
            	总结:
                	1.serializers.Serialzier无法插入数据,需要自己实现create
                	2.字段太多,不能自动序列化
        	 - get 单条数据接口设计
            	1.定义url
                2.获取数据对象
                3.开始序列化:ser_data = BookSerializer(instance=book_obj)
            	4.返回数据: return Response(ser_data.data)
             - delete 
             - put
            	1.定义url
                2.获取数据对象
                	2.1 book_obj = Book.objects.get(pk=1)
                3.开始序列化(验证数据,save(0))
                	3.1ser_data = BookSerializer(instance=book_obj, many=False)
            	4.返回数据: return Response(ser_data.data)
            		
    
    serializers.py
    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
            fields = "__all__"
            extra_kwargs = {
                # "publish": {"write_only": True},
                # "authors": {"write_only": True},
            }
        publish_name = serializers.CharField(read_only=True, max_length=32, source="publish.name")
        city_name = serializers.CharField(read_only=True, max_length=32, source="publish.city")
        # authors = serializers.SerializerMethodField()
    
        # def get_authors(self, instance):
        #     author_list = []
        #     for author in instance.authors.all():
        #         author_list.append(author.name)
        #     return author_list
    

    6.今日内容:

    今日内容:
    	1.接口设计
        2.视图组件
        	- 试图组件是用来优化接口逻辑的
    前提提要:
    	- 多继承
    今日详细:
    	- 使用试图组件进行接口优化
        	- 使用试图组件mixin进行接口逻辑优化
            	from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin
                from rest_framework.generics import GenericAPIView
                
                class BookView(ListModelMixin, CreateModelMixin, GenericAPIView):
                class BookView(ListModelMixin, CreateModelMixin, GenericAPIView):
                      queryset = Book.objects.all()
                      serializer_class = BookSerializer
    
                      def get(self, request, *args, **kwargs):
                          return self.list(request, *args, **kwargs)
    
                      def post(self, request, *args, **kwargs):
                          return self.create(request, *args, **kwargs)
            	注意:单条数据操作的url是这样的re_path('^book/(?P<pk>\d+)/$', views.BookFilterView.as_view()),
            	class BookView(ListModelMixin, CreateModelMixin, GenericAPIView):
                    queryset = Book.objects.all()
                    serializer_class = BookSerializer
    
                    def get(self, request, *args, **kwargs):
                        return self.list(request, *args, **kwargs)
    
                    def post(self, request, *args, **kwargs):
                        return self.create(request, *args, **kwargs)
    			class BookFilterView(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView):
                    queryset = Book.objects.all()
                    serializer_class = BookSerializer
    
                    def get(self, request, *args, **kwargs):
                        return self.retrieve(request, *args, **kwargs)
    
                    def delete(self, request, *args, **kwargs):
                        return self.destroy(request, *args, **kwargs)
    
                    def put(self, request, *args, **kwargs):
                        return self.update(request, *args, **kwargs)
            - 使用试图组件的view进行接口逻辑优化
            	- 导入模块
                from rest_framework import generic
                - 写视图类
            	class BookView(generics.ListCreateAPIView):
                    queryset = Book.objects.all()
                    serializer_class = BookSerializer
    
    
                class BookFilterView(generics.RetrieveUpdateDestroyAPIView):
                    queryset = Book.objects.all()
                    serializer_class = BookSerializer
            
            - 使用viewset优化接口逻辑
                - 编写url
                urlpatterns = [
                    re_path('^book/$', views.BookView.as_view(actions={
                        "get": "list",
                        "post": "create"
                    })),
                    re_path('^book/(?P<pk>\d+)/$', views.BookView.as_view({
                        "get": "retrieve",
                        "put": "update",
                        "delete": "destroy"
                    })),
                    ]
                - 导入模块
                    from rest_framework.viewset import ModelViewSet
               	- 设计视图类
                    class BookView(ModelViewSet):
                        queryset = Book.objects.all()
                        serializer_class = BookSerializer
    

    知识点回顾:

    1.继续设计接口
    	1.1 post接口设计
         	- 如果使用serializer.Serialiezer会有如下问题:
            	1.需要手动插入数据(必须自定义create方法)
                2.手动序列化需要的字段
            - 为了解决上面的问题,我们建议使用serializer.ModelSerializer
            	class BookSerializer(serializers.ModelSerializer):
                    class Meta:
                        model = Book
                        fields = "__all__"
                        extra_kwargs = {
                            # "publish": {"write_only": True},
                            # "authors": {"write_only": True},
                        }
                    publish_name = serializers.CharField(read_only=True, max_length=32, source="publish.name")
                    city_name = serializers.CharField(read_only=True, max_length=32, source="publish.city")
                    authors = serializers.SerializerMethodField()
    
                    def get_authors(self, instance):
                         author_list = []
                         for author in instance.authors.all():
                             author_list.append(author.name)
                         return author_list
    	1.2 delete接口设计
        	class BookView(ApiView):
                def delete(self, request, id):
                    obj = Book.objects.get(pk=id).delete()
                   	reutrn Response()
        1.3 put接口设计
        	class BooKFilterView(APIVIew):
                def put(self,reqeust,id):
                    data = request.data
                    book_obj = Book.objects.get(pk=id)
                    
                    ser_data = BookSerializer(data=request.data, instance=book_obj,many=Flase)
                    
                    if ser_data.is_valid():
                    	ser_data.save()
                    	return Response(res_data.data)
                    else:
                        return Response(res_data.errors)
        1.4 get获取单条
        	class BooKFilterView(APIVIew):
                
                def get(self,reqeust,id):
                    book_obj = Book.objects.get(pk=id)
                    ser_data = BookSerializer(instance=book_obj,many=Flase)
                    return Response(ser_data.data)
    
    2.视图组件(mixin, genericview, viewset)
    	视图组件是用来优化接口逻辑的
    	2.1 使用mixin的使用
        	- 导入 
            	from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestoryModelMixin,RetireveModelMixin
                from rest_framework.genericview import GenericAPIView
            - 设计视图类
            	class BooKView(ListModelMixin, CreateModelMixin, GenericAPIView):
                    serializer_classes = BooKSerialzier
                    queryset = Book.objects.all()
                    
                    def get(self, request, *args, **kwargs):
                        return self.list(request, *args, **kwargs)
                    def post(self, request, *args, **kwargs):
                        return self.create(request, *args, **kwargs)
       	2.2 mixin源码剖析
    

    总结:

    day:1
        - 1.什么是编程
        - 2.什么是rest
        - 3.BS架构中,客户端通过url来访问客户端的资源(数据)
        - 4.传统的url中设计里面包含动词(理论上来说,可以实现业务需求)
        - 5.rest
        	url是唯一定位资源,http请求方式描述用户行为
            GET:127.0.0.1:8001/books/    # 获取所有数据    
            GET:127.0.0.1:8001/books/id/ # 获取单挑数据
            POST:127.0.0.1:8001/books/   # 增加数据   
            DELETE:127.0.0.1:8001/books/id/ # 删除数据
            UPDATE:127.0.0.1:8001/books/id/ # 更改数据  
            返回规范:
            GET:127.0.0.1:8001/books/    # 获取所有数据    返回[{},	 ,]
            GET:127.0.0.1:8001/books/id/ # 获取单挑数据	 返回{}单条数据
            POST:127.0.0.1:8001/books/   # 增加数据       返回{}添加成功
            DELETE:127.0.0.1:8001/books/id/ # 删除数据    返回‘’返回空
            put/UPDATE:127.0.0.1:8001/books/id/ # 更改数据    返回更新后的完整数据
        	错误处理
            {"error":"error_message"}
         - 6.CBV
        	- 定义view.py 
            	from django.views import View
                class BookView(View):
                    
                    def get(self. request):
                        pass
                    
                    def post(self, request):
                        pass
            - 定义url.py
            	from django.urls import re_path
                
                urlpatterns = [
                    re_path("`book/$", views.BookVIew.as_view()),
                ]
                
                @classonlymethod
                def as_view(cls, *arg, **kwargs):
                    def view(request):
                        self = cls(**args)
                        return self.dispath()
                   	return view
                
                def dispatch(self,request):
                    if hasattr(self, self.method.lower(),):
                        handler = getattr(self, self.method.lower())
                    else:
                        raise
                    return handler(reqeust, *args, **kwargs)
        - 7.django restframework 
        	- 本质上是一个django app 用来帮助我们更快的开发出符合rest规范的web app
        	- pip install django
            - pip install djangorestframework
            - 定义views.py 
            	from rest_framework.views import APIVIew
                class ApiView(view);
                	@classonlymethod
                    def as_view(cls, request, *args, *8kwargs);
                    	view = super(APIView, cls).as_view()
                        return view
                    def dispatch(self,request):
                        if hasattr(self, self.method.lower(),):
                            handler = getattr(self, self.method.lower())
                        else:
                            raise
                        return handler(reqeust, *args, **kwargs)
                class BookView(APIView):
                    def get(self, reqeust):
                        pass
            - 定义url.py
            	from django.urls import re_path
                
                url_parttrens = [
                    re_path("books/`$", BookView.as_view()),
                ]
       	- 8.drf解析器
    		- 定义views.py
            	from django.https import  JsonResponse 
       			from rest_framework.views import APIView
                from rest_framework.reqeust import Request
                
                class Request(request):
                    
                    def __init__(self, parsers=get_parser()):pass
                    
                    def _parse(self):
                        parser = self.negotiator.select_parser(self, self.parsers)
                        parsed = parser.parse(stream, media_type, self.parser_context)
                        
                    	return parsed.data, parsed
                    .files
                    
                    def _laod_data_and_file(self)
                    	self.data, self.files = self._parse()
                        self._full_data = self.data
                    	
                    @proparty
                    def data(self):
                        if not hasattr(self, "full_data"):
                        	self._laod_data_and_files()
                        return self._full_data
                
                class APIVIew(View):
                    
                    parser_classes = apisettings.DEFAULT_PARSERS_CLASSES
                    
                    def get_parsers(self):
                        return [parser() for parsr in self.parser_classes]
                    
                    def initialize_request(self,request):
                        return Request(request,
                                parser = self.get_parsers())
                    
                    def dispatch(self, request):
                        request = self.initialize_request(request, *args, **kwargs)
                        self.request = request
                		return response()
                    
                class BookView(APIVIew):
                    def get(self. request):
                        pass
                    
                    def post(self, request):
                        request.data
                        return JsonResponse()
                    
    
    day2:
        1.解析器组件
        2.序列化组件
        	2.1 django原生的序列化
            	- 导入
                from django.core serialziers import serialize
                class BooVIew(APIVIew):
                    def get(self, request):
                        books = Book.object.all()
                        ser_data = serialzie("json", books)
    					return HttpResponse(ser_data)
            2.2 drf序列化组件
            	- 导入
                from rest_framework import serialzers
                - 定义序列化类
                class BooKSerializer(serializers.Serializer):
                    title = serializers.Charfield(max_length=32)
                    price = serializers.DecimalField()
                    publish = serialziers.CahrField(max_Lenght=32)
                    publish_name = serializers.CharField(max_lenght=32, source="publish.name", read_only=True)
                    authors_list = serializers.SerializerMethodField()
                    
                    def get_authors_lsit(self, instance):
                        authors = list()
                        for author in instance.authors():
                            authors.append(author.name)
                       	return authors
                    def create(self, verified_data):
                        pass
                    
                    def updata(self, verified_data):
                    	pass
                        
                - 定义view.py
                class BoosView(APIView):
                    def get(self, request):
                        books = Book.object.all()
                        ser_data =  BooKserialzier(books, many=True)
                        reutnr Response(ser_data.data)
            2.3 通过drf的序列化组件进行接口设计
            2.4	通过drf的序列化组件进行POST接口设计
            	- 定义view。py
                	class BoosVIew(APIVIew);
                    	def post(self, request):
                            data = request.data
                            ser_data = BooKSerialzier(data=data, many=False)
                            if ser_data.is_valid():
                                # BooKSerialzier没有实现写入操作即就是create方法
                                ser_data.save()
                                reutnr Response(ser_data.data)
                            else:
                                reutnr Response(ser_data.errors)
                        def put(self, request, id):
                            data = request.data
                            book_obj = Book.objects.get(pk=id)
                            ser_data = BooKSerialzier(instance=book_obj,data=data, many=False)
                            if ser_data.is_valid():
                                ser_data.save()
                             	reutnr Response(ser_data.data)
                            else:
                                reutnr Response(ser_data.errors)
    
    day3:
    	1.通过drf的序列化组件进行put接口设计
    	def put(self, request, id):
            data = request.data
            book_obj = Book.objects.get(pk=id)
            ser_data = BooKSerialzier(instance=book_obj,data=data, many=False)
            if ser_data.is_valid():
                ser_data.save()
                reutnr Response(ser_data.data)
                else:
                    reutnr Response(ser_data.errors)
    	2.通过drf的序列化组件进行delete接口设计
    	3.通过drf的序列化组件进行get接口设计
        4.试图组件
        	- 视图组件是用来优化接口逻辑的
        	4.1 mixins
            	- 导入
                	from rest_framework.mixins import (ListModelMixin, 						
                                                       CreateModelMixin,
                                                       UpdateModelMixin, 
                                                       RetrieveModelMixin,
                                                       DestoryModelMixin)
                    from rest_framework.generics import GenericAPIView
                - 定义序列化类
                - urls.py
                	re_path('^book/$', views.BookView.as_view()),
        			re_path('^book/(?P<pk>\d+)/$', views.BookFilterView.as_view()),
                - 定义view.py 
                	class BookView(ListModelMixin, CreateModleMixin, GenericAPIVIiew):
                        queryset = Book.objects.all()
                        serialzier_class = BookSerializer
                        
                        def get(self, request):
                            pass
                        def psot(self, request):
                            pass
                        
                    class BookView(UpdateModelMixin, 
                                   RetrieveModelMixin, 
                                   DestoryModelMixin, 
                                   GenericAPiVIew):
                        queryset = Book.objects.all()
                        serialzier_class = BookSerializer
                        
                        def get(self, request, pk):
                            return self.retrieve(request)
                        def put(self, request, pk):
                            return self.update(request)
                        def delete(self, request, pk):
                            return self.destory(request)
            4.2 genericview
            	- 导入 
                from rest_framework import generics
                 class BookView(generics.ListCreateAPIView):
                        queryset = Book.objects.all()
                        serialzier_class = BookSerializer
                 class BookView(generics.RetrieveUpdateDestroyAPIView):
                        queryset = Book.objects.all()
                        serialzier_class = BookSerializer
            4.3 viewset
            	- 导入 
              	from rest_framework.viewsets import ModelViewSet
                class BookView(ModelViewSet):
                        queryset = Book.objects.all()
                        serialzier_class = BookSerializer
                - url.py
                urlpatterns = [
                    re_path('^book/$', views.BookView.as_view(actions={
                        "get": "list",
                        "post": "create"
                    })),
                    re_path('^book/(?P<pk>\d+)/$', views.BookView.as_view({
                        "get": "retrieve",
                        "put": "update",
                        "delete": "destroy"
                    })),
                ]
    

    7.DRF之认证频率组件

    7.1 认证类

    1.认证类
    	1.1 使用方式
        	- 定义一个认证类
            # 第一步:认证类
            class UserAuth():
                def authenticate_header(self, request):
                    pass
    
                # 认证逻辑
                def authenticate(self, request):
                    user_token = request.query_params.get("token")
                    try:
                        token = UserToken.objects.filter(token=user_token).first()
                        return token.user.username, token.token
                    except Exception:
                        raise APIException("没有认证")
    	   - 在需要认证的数据接口里面指定认证类	
            class BookView(ModelViewSet):
                # 应用
                authentication_classes = [UserAuth, ]
                queryset = Book.objects.all()
                serializer_class = BookSerializer
        1.2 源码流程
        	1.初始化request
            	def initialize_request(self, request, *args, **kwargs):
                    parser_context = self.get_parser_context(request)
    
                    return Request(
                        request,
                        parsers=self.get_parsers(),
                        authenticators=self.get_authenticators(),
                        negotiator=self.get_content_negotiator(),
                        parser_context=parser_context
                    )
            2. 通过requestt触发认证
                self.perform_authentication(request)
                def perform_authentication(self, request):
                    request.user 
            3. 执行user属性方法
                def user(self):
                    if not hasattr(self, '_user'):
                        with wrap_attributeerrors():
                            self._authenticate()
                    return self._user
                
                # 赋值操作的时候执行
                @user.setter
                def user(self, value):
                    self._user = value
                    self._request.user = value
            4. 执行 self._authenticate()
            	def _authenticate(self):
                    for authenticator in self.authenticators:
                        try:
                            # 这里调用自己写的认证类的authenticate方法进行认证
                            user_auth_tuple = authenticator.authenticate(self)
                            except exceptions.APIException:
                                self._not_authenticated()
                                raise
                                # 只要返回值不是None就可以通过认证
                                if user_auth_tuple is not None:
                                    self._authenticator = authenticator
                                    self.user, self.auth = user_auth_tuple
                                    return
                                self._not_authenticated()    
        
        
    
    class Person():
    	@property
        def name(self):
            return self._name
        
        @name.setter
        def name(self, value):
            self._name = value
    

    如何全局设置:

    1.创建一个authencitation_classes.py文件
    	class UserAuth():
        def authenticate_header(self, request):
            pass
    
        # 认证逻辑
        def authenticate(self, request):
            user_token = request.query_params.get("token")
            try:
                token = UserToken.objects.filter(token=user_token).first()
                # 这里因为有可能用到一下数据所以返回下面的数据,如果只是为类认证可以随意返回(不为None即可)
                return token.user.username, token.token
            except Exception:
                raise APIException("没有认证")
    
    2.在settings.py文件中全局引入
        REST_FRAMEWORK = {
                'DEFAULT_PARSER_CLASSES': [
                    'rest_framework.parsers.JSONParser',
                    'rest_framework.parsers.FormParser',
                    'rest_framework.parsers.MultiPartParser'
                ],
                'DEFAULT_AUTHENTICATION_CLASSES': [
                    'myapp.authencitation_classes.UserAuth',
                ],
        }
    

    注意事项:

    假如有多个认证类(权限类)的话,我们只需要在最后一个认证类返回结果即可,因为在上面的第四步中,如果前面返回了正确的结果,他就不再循环验证后面的认证类。(其中认证类和权限类都有这个问题请注意)

    4. 执行 self._authenticate()
            	def _authenticate(self):
                    for authenticator in self.authenticators:
                        try:
                            # 这里调用自己写的认证类的authenticate方法进行认证
                            user_auth_tuple = authenticator.authenticate(self)
                            except exceptions.APIException:
                                self._not_authenticated()
                                raise
                                # 只要返回值不是None就可以通过认证
                                if user_auth_tuple is not None:
                                    self._authenticator = authenticator
                                    self.user, self.auth = user_auth_tuple
                                    return
                                self._not_authenticated()   
                                
    - 在需要认证的数据接口里面指定认证类	
            class BookView(ModelViewSet):
                # 应用
                authentication_classes = [UserAuth, UserAuth]  # 多个认证类
                queryset = Book.objects.all()
                serializer_class = BookSerializer
    

    7.2 权限类

    1.权限类
    	1.1 使用方式
        	- 定义一个权限类
            # 第一步:权限类
            class UserPermission:
                message = "你没有查看数据权限"
                # 权限逻辑
                def has_permission(self, request, view):
                    if request.user.user_type == 1:
                        return 1
                    return None
                
    	   - 在需要认证的数据接口里面指定认证类	
            class BookView(ModelViewSet):
                # 应用
                authentication_classes = [UserAuth, ]
                queryset = Book.objects.all()
                serializer_class = BookSerializer
                
        1.2 源码流程
        	- 触发地点APIView的dispatch中
            	self.initial(request, *args, **kwargs)
                def initial(self, request, *args, **kwargs):
                    self.perform_authentication(request)
                    self.check_permissions(request) # 权限检验
                    self.check_throttles(request)
            - 权限检验函数
                def check_permissions(self, request):
                    for permission in self.get_permissions():
                        # 这里可以看到permission就是我们写的权限类
                        # 下面触发了has_permission方法传入了request和view,只要返回的不为False就可以通过
                        if not permission.has_permission(request, self):
                            self.permission_denied(
                                request, message=getattr(permission, 'message', None)
                            )
    

    知识点回顾:

    1.认证组件
    
    	1.1 权限组件的使用
        	- 写一个认证类
    			class UserAuth():
                    def authenticate_header(self, request):
                        pass
                    
                    def authenticate(self, request):
                        # 通过
                        # 1.拿到用户传递的数据
                        # 2.拿到数据里面的token与用户传递的token进行比较
                        # 不通过
                        raise APIException
             - 在试图类中指定认证类
            	class BookView(APIVIew):
                    authentication_classes = [UserAuth,]
             - 可以指定多个认证类,需要注意的是,如果需要返回数据,请在最后一个认证类中返回
        1.2 全局认证
        	- 在settiongs文件中指定
            REST_FRAMEWORK = {
                    'DEFAULT_PARSER_CLASSES': [
                        'rest_framework.parsers.JSONParser',
                        'rest_framework.parsers.FormParser',
                        'rest_framework.parsers.MultiPartParser'
                    ],
                    'DEFAULT_AUTHENTICATION_CLASSES': [
                        'myapp.authencitation_classes.UserAuth',
                    ],
            }
    2.权限组件
    	2.1 权限组件的使用
        	-定义个权限类
            	class UserPerm():
                    def has_permission(self,request, view):
                        if 有权限:
                        	return True
                        else:
                            return False
            - 在试图类中指定权限类
            	class BookView(APIVIew):
                    permission_classes = [UserPerm,]
            - 可以指定多个权限类,需要注意的是,如果需要返回数据,请在最后一个权限类中返回
    3.频率组件
        3.1 频率组件的使用
                -定义个频率类
                    class RateThrott():
                        def alow_reqeust(self,request, view):
                            if 没有超过频率:
                                return True
                            else:
                                return False
                - 在试图类中指定频率类
                    class BookView(APIVIew):
                        permission_classes = [RateThrott,]
                - 可以指定多个频率类,需要注意的是,如果需要返回数据,请在最后一个频率类中返回
      	3.2 使用DRF的简单频率空值来控制用户的访问频率
        	- 导入模块
        		from rest_framework.throttling import SimpleRateThrottle
            - 定义并继承SimpleRateThrolle
            	class MYThrottle(SimpleRateThrottle):
                    # 指定访问频率
                    rate = "5/m"
                    # 通过什么方式区分用户
                    def get_cache_key(self, request, view):
                        return self.get_ident(request)
            - 在试图类中指定频率类
                    class BookView(APIVIew):
                        permission_classes = [MYThrottle,]
                - 可以指定多个频率类,需要注意的是,如果需要返回数据,请在最后一个频率类中返回
       3.3 全局方式
    		- 导入模块
        		from rest_framework.throttling import SimpleRateThrottle
            - 定义并继承SimpleRateThrolle
            	class MYThrottle(SimpleRateThrottle):
                    scope = "visit_rate"
                    def get_cache_key(self, request, view):
                        return self.get_ident(request)
            - 在settiongs文件中指定
            	REST_FRAMEWORK = {
                        'DEFAULT_THROTTLE_CLASSES': [],
                        'DEFAULT_THROTTLE_RATES': {
                                'visit_rate': "5/m",
                                'anon': None,
                            },
                }
    		- 源码例程
            	- 1.通过dispatch中的inintial方法中调用
                	self.check_throttles(request)
                - 2.拿到频率器类之后访问其中的allow_request方法
                	def check_throttles(self, request):
                        throttle_durations = []
                        for throttle in self.get_throttles():
                            # 执行allow_request方法
                            if not throttle.allow_request(request, self):
                                throttle_durations.append(throttle.wait())
                        if throttle_durations:
                            durations = [
                                duration for duration in throttle_durations
                                if duration is not None
                            ]
    
                            duration = max(durations, default=None)
                            self.throttled(request, duration)
                - 3.自己的类中没有allow_reqeust方法,去SimpleRateThrottle中找
                	def allow_request(self, request, view):
                        if self.rate is None:
                            return True
    
                     	# 获取一个唯一的key值,给没给用户进行标识
                        self.key = self.get_cache_key(request, view)
                        if self.key is None:
                            return True
    					# 通过缓存拿取值
                        self.history = self.cache.get(self.key, [])
                        self.now = self.timer()
                        # 访问时间和现在时间减去间隔时间进行比较,如果小于呢个时间就弹出数据
                        # 可以理解为前一访问的时间+间隔时间<现在的时间(说明这是间隔时间已经够久了)
                        # 这时候说明这是时间是很久之前访问的,留在历史里面没用了,为了方便继续插入时间,弹出这个时间
                        while self.history and self.history[-1] <= self.now - self.duration:
                            self.history.pop()
                        if len(self.history) >= self.num_requests:
                            return self.throttle_failure()
                        return self.throttle_success()
                    def throttle_success(self):
                        # 这里就是插入时间的地方,这代码写的很简洁(但是理解起来又点绕)
                        self.history.insert(0, self.now)
                        self.cache.set(self.key, self.history, self.duration)
                        return True
                    
                    而上面的self.num_requests哪里来的呢?在构造方法中可以看到,对self.rate进行了解析:
                    def __init__(self):
                        if not getattr(self, 'rate', None):
                            self.rate = self.get_rate()
                        self.num_requests, self.duration = self.parse_rate(self.rate)
                   # 如果没有发现rate值,就会去找scope,这个值是全局设置的时候使用的
                    def get_rate(self):
                		return self.THROTTLE_RATES[self.scope]
                   # 解析的结果是什么呢?其实就是将我们设置的rate进行分割
                    def parse_rate(self, rate):
                        if rate is None:
                            return (None, None)
                        num, period = rate.split('/')
                        num_requests = int(num)
                        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
                        return (num_requests, duration)
                - 4. 在第二部中,发现如果结果返回为非真值
                	 if not throttle.allow_request(request, self):
                       throttle_durations.append(throttle.wait())
                     def wait(self):
                        if self.history:
                            remaining_duration = self.duration - (self.now - self.history[-1])
                        else:
                            remaining_duration = self.duration
    					# 这里是对访问次数有进行了一次处理,如果访问次数大于设置的限制次数久之就返回None
                        # 其实进行测试之后发现,他是为了对如果我们设置的限制次数是负数的一种处理
                        available_requests = self.num_requests - len(self.history) + 1
                        if available_requests <= 0:
                            return None
                        return remaining_duration / float(available_requests)
    

    关于自定义频率访问:

    import math
    import time
    
    from django.utils.translation import gettext_lazy as _
    
    from rest_framework import exceptions
    from rest_framework.throttling import SimpleRateThrottle
    
    from myapp.CustomException import MyThrottled
    
    
    # 自定义频率类
    class VisitThrottle():
        user_visit_information = dict()
        visited_times = 1
        period = 60
        allow_times_per_minute = 5
        first_time_visit = True
    
        def allow_request(self, request, view):
            self.request_host = request_host = request.META.get("REMOTE_ADDR")
            current_user_info = self.user_visit_information.get(request_host, None)
    
            if not self.__class__.first_time_visit:
                self.user_visit_information[request_host][0] += 1
                current_user_times = self.user_visit_information[request_host][0]
    
                # 如果访问次数,大于设置的次数就直接返回True
                if current_user_times > self.allow_times_per_minute:
                    # 这就是第一次访问的时候的时间和当前访问的事件进行比较,如果在有效期间内
                    if self._current_time - current_user_info[1] <= self.period:
                        # 就更新或者设置已经过了多久的时间
                        if len(current_user_info) > 2:
                            current_user_info[2] = self._time_left
                        else:
                            current_user_info.append(self._time_left)
                        view.throttled = self.throttled
                        return None
                    else:
                        self.__class__.first_time_visit = True
            # self.__class__是更新类变量而不是实例变量
            if self.first_time_visit:
                self.__class__.first_time_visit = False
                self._initial_information()
    
            return True
    
        # 该函数返回一个响应值,用于给客户端展示,剩余多少秒可以尽心下次访问
        def wait(self):
            return self.period - self.user_visit_information[self.request_host][2]
    
        def throttled(self, request, wait):
            raise MyThrottled(wait)
    
        @property
        def _current_time(self):
            return time.time()
    
        @property
        def _time_left(self):
            return math.floor(self._current_time - self.user_visit_information.get(self.request_host)[1])
    
        def _initial_information(self):
            self.user_visit_information[self.request_host] = [self.visited_times, self._current_time]
    
    
    # 使用rest_framework的频率类
    class MYThrottle(SimpleRateThrottle):
        rate = "5/m"
    
        def get_cache_key(self, request, view):
            # 给请求的view挂在一个处理函数
            view.throttled = self.throttled
            return self.get_ident(request)
    
        def throttled(self, request, wait):
            raise MyException(wait)
    
            
    # 这个是覆盖提示信息的类
    class MyException(exceptions.Throttled):
        default_detail = _("请求{wait}频率太快")
        extra_detail_singular = extra_detail_plural = _('请{wait}秒之后访问.')
    
        def __init__(self, wait=None, detail=None, code=None):
            # 走一下父类
            super(MyException, self).__init__(wait, detail, code)
    

    8.url注册其使用

    - 导入模块
        from rest_framework import routers
        from django.urls import re_path, include
    	from myapp import views
    - 生成一个注册器实例对象
    	router = routers.DefaultRouter()
    - 将需要自动生成url的接口注册
    router.register("books", views.BookView)
    - 自动生成url
    urlpatterns = [
        re_path("^", include(router.urls))
    ]
    

    9.响应器组件

    - 导入模块
    	from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
    - 使用:
    	class BookView(ModelViewSet):
        	renderer_classes = [JSONRenderer]
    

    10.分页器组件

    1.分页器
    	1.1 简单使用
        - 导入模块
            from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination
        - 获取取数据
            books = Book.objects.all()
        - 创建一个分页器对象
            paginator = PageNumberPagination()
        - 开始分页
            paged_books = paginator.paginate_queyset(books, request)
        - 开始序列化
            ser_data = BookSerializer(books, many=True)
        - 使用:
            return Reponse(ser_data)
        1.2 分页器组件局部使用
            page_size = 2   # 每页显示多少条数据
            page_query_param = "p"  # 跳转到第几页(临时的通过url输入的)?p=3&size=3
            page_size_query_param = "size"  # 每页显示的数据条数(临时的通过url输入的)
            max_page_size = 5  # 每页最多显示多少条数据
        - 导入模块
            from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination
            - 定义一个分页器类继承PageNumberPagination
            class MyPaginator(PageNumberPagination):
                page_size = 2
                page_query_param = "p"
                page_size_query_param = "size"
                max_page_size = 5
    	- 获取取数据
            books = Book.objects.all()
        - 实例化一个分页类对象
        	paginagor = MyPaginator()
        - 开始分页
            paged_books = paginator.paginate_queyset(books, request)
        - 开始序列化
            ser_data = BookSerializer(books, many=True)
        1.3 全局使用
        	REST_FRAMEWORK = {
            "PAGE_SIZE": 3,
            'DEFAULT_PAGINATION_CLASS': "myapp.paginator_classes.MyPaginator",   
    }
       	1.4 关于分页器的源码解析
        	- 1.其中一个ListModelMixin中的分页器源码的流程
            	def list(self, request, *args, **kwargs):
                    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)
            - 2.分页器真正的逻辑部分
            	 def paginate_queryset(self, queryset):
                    if self.paginator is None:
                        return None
                    # 这里paginate_queryset才是我们自定义的分页器类的触发函数
                    return self.paginator.paginate_queryset(queryset, self.request, view=self)
            	@property
                def paginator(self): 
                    if not hasattr(self, '_paginator'):
                        if self.pagination_class is None:
                            self._paginator = None
                        else:
                            # 通过self.pagination_class()拿到我们在BookVIew中挂载的分页器类
                            self._paginator = self.pagination_class()
                    return self._paginator   
             - 3.返回的数据
            	if page is not None:
                    serializer = self.get_serializer(page, many=True)
                    return self.get_paginated_response(serializer.data)
    
    
  • 相关阅读:
    未能正确加载“Microsoft.VisualStudio.Editor.Implementation.EditorPackage”
    未能正确加载“Microsoft.VisualStudio.Editor.Implementation.EditorPackage”包
    重装VS2010时出现未能正确加载 "radlangsvc.package,radlangsvc.vs...
    page.Response.WriteFile(newpath);
    Response.ContentType 详细列表 <转>
    创建存储过程,使用游标更新表信息
    淘宝顶端的通知样式 .
    ssm整合各配置文件
    XSS-Labs(Level1-10)
    局域网技术
  • 原文地址:https://www.cnblogs.com/liuzhanghao/p/11976707.html
Copyright © 2011-2022 走看看