from rest_framework.generics import GenericAPIView
from rest_framework.request import Request
from rest_framework import filters
from rest_framework import mixins
from rest_framework import generics
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework import status

from .models import Project
from . import serializers
# from .filters import ProjectFilter
from utils.pagination import PageNumberPagination

# from utils import mixins
"""  來自views3.py文件中的內容,針對GenericAPIView類視圖
class ProjectView(GenericAPIView):
 可以繼承DRF中的APIView視圖
 1.APIView為View的子類
 2.每一個實例方法的第二個參數為Request對象
 3.Request在Django的HttpRequest之上做了拓展
     》與HttpRequest中解析參數的方法完全兼容
     》解析查詢字符串參數:GET   ->  query_params
     》解析application/x-www-form-urlencoded參數:POST   ->  data
     》解析application/json參數:body   ->  data
     》解析multipart/form-data參數:POST、FILES   ->  data
 4.提供了認證、授權、限流功能
 5.返回DRF中的Response
     》為HttpResponse子類
     》可以自動根據請求頭中的Accept字段,返回相應格式的數據
     》data接受序列化輸出的數據(字典、嵌套字典的列表)
     》status指定響應狀態碼
     》headers修改響應頭信息(字典)
 6.解析器類
     》提供針對請求頭中Content-Type參數,自動解析請求參數
     》默認的解析器有三種:JSONParser(application/json)、FormParser(application/x-www-form-urlencoded)、
         MultiPartParser(multipart/form-data)
     》有兩種方式可以修改使用的解析器類:
         方式一:全局settings.py中REST_FRAMEWORK -> DEFAULT_PARSER_CLASSES中指定需要使用的解析器類
         方式二:在具體某個類視圖中指定parser_classes類屬性(列表),優先級高於方式一

 7.渲染器類
     》提供針對請求頭中Accept參數,自動選擇需要的渲染器,將數據以特定的格式返回
     》默認的渲染器類有二種:JSONRenderer(application/json)、BrowsableAPIRenderer(text/html)
         如果未指定Accept或者指定的Accept不為text/html,默認返回json數據
     》有兩種方式可以修改使用的解析器類:
         方式一:全局settings.py中REST_FRAMEWORK -> DEFAULT_RENDERER_CLASSES中指定需要使用的渲染器類
         方式二:在具體某個類視圖中指定renderer_classes類屬性(列表),優先級高於方式一

 8.GenericAPIView類視圖
     》是APIView的子類,繼承了APIView所有功能(認證、授權、限流、Request、Response、解析器、渲染器)
     》提供了獲取列表數據的相關功能(過濾、排序、分頁)
     》往往需要指定queryset類屬性(定義當前類視圖操作的查詢集對象)
     》往往需要指定serializer_class類屬性(定義了當前類視圖使用的公共序列化器類)
     》使用get_queryset()方法獲取queryset類屬性、使用get_serializer()方法獲取serializer_class類屬性
     》提供了get_object()方法,獲取某一個模型對象
     》在定義url路由時,指定接收主鍵值的關鍵字參數名稱,默認為pk,如果不為pk的話,必須重寫lookup_url_kwarg
     》一般lookup_field類屬性不需要修改(默認為pk),指定的是過濾模型對象時,使用的關鍵詞參數名稱

 9.實現搜索過濾功能
     》指定過濾引擎,有兩種方式
         方式一:在全局settings.py中指定DEFAULT_FILTER_BACKENDS(列表,指定過濾引擎的絕對路徑字符串)
         方式二:在類視圖中filter_backends類屬性(列表,指定過濾引擎的引用),優先級高於全局
     》必須指定search_fields類屬性,定義待校驗的字段(模型類中的字段名字符串),默認忽略大小寫的包含過濾
         '^': 'istartswith',
         '=': 'iexact',
         '@': 'search',
         '$': 'iregex',
     》如果不指定search_fields類屬性,不會進行搜索過濾
     》調用視圖的filter_queryset()方法,對查詢集進行過濾,需要接收queryset查詢集參數
     》在全局settings.py中使用SEARCH_PARAM參數指定前端過濾查詢參數(默認為search)

 10.實現排序過濾功能
     》指定過濾引擎,有兩種方式
         方式一:在全局settings.py中指定DEFAULT_FILTER_BACKENDS(列表,指定過濾引擎的絕對路徑字符串)
         方式二:在類視圖中filter_backends類屬性(列表,指定過濾引擎的引用),優先級高於全局
     》必須指定ordering_fields類屬性,定義支持排序的字段(模型類中的字段名字符串)
     》如果不指定ordering_fields類屬性,那麼支持由 serializer_class 屬性指定的序列化器上的任何可讀字段進行排序
     》指定ordering_fields = '__all__',指定視圖應允許對模型的所有字段進行排序
     》指定ordering類屬性,定義默認的排序字段;也可以 Projects.objects.all().order_by('name')
     》調用視圖的filter_queryset()方法,對查詢集進行過濾,需要接收queryset查詢集參數
     》在全局settings.py中使用ORDERING_PARAM參數指定前端過濾查詢參數(默認為ordering)

 11.實現分頁功能
     》指定分頁引擎類,有兩種方式
         方式一:在全局settings.py中指定DEFAULT_PAGINATION_CLASS(指定分頁引擎的絕對路徑字符串)
         方式二:在類視圖中pagination_class類屬性(指定分頁引擎的引用),優先級高於全局
     》必須在settings.py中DEFAULT_PAGINATION_CLASS指定PAGE_SIZE參數(指定默認每一頁顯示的數據條數)或者
         在自定義分頁引擎類指定page_size參數,如果未指定,那麼分頁功能不會開啓
     》在獲取列表數據實例方法中調用paginate_queryset方法,需要接收queryset查詢集對象,會返回嵌套page對象的列表
     》調用get_paginated_response方法將分頁數據返回,需要接收serializer.data參數
     》如果需要開啓前端能夠指定獲取每一頁的數據條數,往往需要重寫分頁引擎類PageNumberPagination
         》page_size指定默認每一頁數據條數
         》page_query_param設置前端指定頁碼的查詢字符串參數名稱
         》page_query_description,對前端指定頁碼的查詢字符串參數的中文描述
         》必須重寫page_size_query_param類屬性,才能開啓前端能夠指定獲取每一頁的數據條數的功能
         》page_size_query_description是page_size_query_param類屬性的中文描述
         》max_page_size指定前端每一頁數據最大條數的最大值
     》如果需要定製分頁數據的返回,那麼就需要重寫get_paginated_response方法
"""


"""

# 源碼中mixins.py文件中的Mixin拓展類
獲取列表數據:  ListModelMixin拓展類     ->  list action方法      -> get
創建數據:     CreateModelMixin拓展類   ->  create action方法    -> post
獲取詳情數據:  RetrieveModelMixin拓展類 ->  retrieve action方法  -> get
更新數據:     UpdateModelMixin拓展類   ->  update action(完整更新)、partial_update action方法(部分更新) -> put、patch
刪除數據:     DestroyModelMixin拓展類  ->  destroy action方法   -> delete


# 源碼generics.py文件中的APIView具體的通用視圖
獲取列表數據:  ListAPIView         -> 繼承ListModelMixin、GenericAPIView
創建數據:     CreateAPIView       -> 繼承CreateModelMixin、GenericAPIView
獲取詳情數據:  RetrieveAPIView     -> 繼承RetrieveModelMixin、GenericAPIView
更新數據:     UpdateAPIView       -> 繼承UpdateModelMixin、GenericAPIView
刪除數據:     DestroyAPIView      -> 繼承DestroyModelMixin、GenericAPIView


# 源碼viewsets.py文件中的ModelViewSet視圖集, GenericViewSet繼承了GenericAPIView和ViewSetMixin,相當於把上面做了一個整合
class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.


# 源碼generics.py文件中的APIView具體的混合通用視圖
獲取列表數據、創建數據:ListCreateAPIView
獲取數據詳情、更新數據:RetrieveUpdateAPIView
獲取數據詳情、刪除數據:RetrieveDestroyAPIView
獲取數據詳情、更新數據、刪除數據:RetrieveUpdateDestroyAPIView


# 視圖集
1.如果需要實現在定義路由條目時,請求方法與要調用的action方法進行一一對應,必須得繼承ViewSetMixin
2.ViewSet繼承了ViewSetMixin、APIView,具備請求方法與要調用的action方法進行一一對應功能、以及認證授權限流功能,
    但是不支持Mixin,因為沒有提供get_object()、get_queryset()、geat_serializer()等方法
3.GenericViewSet繼承了ViewSetMixin、GenericAPIView,具備請求方法與要調用的action方法進行一一對應功能、以及支持Mixin拓展類
4.ReadOnlyModelViewSet繼承了RetrieveModelMixin、ListModelMixin、GenericViewSet,提供了讀取數據的2個接口
5.ModelViewSet繼承了CreateModelMixin、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin、
    ListModelMixin、GenericViewSet,提供了6個接口
6.ViewSetMixin類提供了請求方法與要調用的action方法進行一一對應功能,在定義路由條目時,在as_view()方法中支持接收字典數據,
    把請求方法名稱字符串作為key,把具體要調用的action方法名字符串作為值
    
"""


# class RetrieveAPIView(mixins.RetrieveModelMixin, GenericAPIView):
#     # get是請求方法名稱
#     # retrieve是action名稱
#     def get(self, request, *args, **kwargs):
#         return self.retrieve(request, args, kwargs)
#
#
# class UpdateAPIView(mixins.UpdateModelMixin, GenericAPIView):
#     def put(self, request, *args, **kwargs):
#         # update是一個action,完整更新
#         return self.update(request, args, kwargs)
#
#
# class DestroyAPIView(mixins.DestroyModelMixin, GenericAPIView):
#     def delete(self, request, *args, **kwargs):
#         return self.destroy(request, args, kwargs)


# class ProjectDetailView(generics.RetrieveAPIView, generics.UpdateAPIView, generics.DestroyAPIView):
class ProjectDetailView(generics.RetrieveUpdateDestroyAPIView):
    serializer_class = serializers.ProjectModelSerializer2
    queryset = Project.objects.all()

    # 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)


# class ProjectView(generics.ListAPIView, generics.CreateAPIView):
class ProjectView(generics.ListCreateAPIView):
    serializer_class = serializers.ProjectModelSerializer2
    queryset = Project.objects.all()
    search_fields = ['name', 'leader', 'desc', 'interfaces__name']
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    # filterset_class = ProjectFilter
    pagination_class = PageNumberPagination
    ordering_fields = ['name', 'id', 'leader']

    # def get(self, request: Request, *args, **kwargs):
    #     return self.list(request, args, kwargs)
    #
    # def post(self, request: Request, *args, **kwargs):
    #     return self.create(request, args, kwargs)


# class ProjectAllView(generics.RetrieveUpdateDestroyAPIView, generics.ListCreateAPIView):
#     serializer_class = serializers.ProjectModelSerializer2
#     queryset = Project.objects.all()
#     search_fields = ['name', 'leader', 'desc', 'interfaces__name']
#     filter_backends = [filters.SearchFilter, filters.OrderingFilter]
#     filterset_class = ProjectFilter
#     pagination_class = PageNumberPagination
#     ordering_fields = ['name', 'id', 'leader']


# 需求:將上面5個接口全部放在同一個類視圖中
# class ViewSet(ViewSetMixin, views.APIView)
# class ProjectViewSet(viewsets.ViewSet):
# class ProjectViewSet(viewsets.ViewSetMixin, generics.GenericAPIView):
# class ProjectViewSet(viewsets.GenericViewSet):
# class ProjectViewSet(
#         mixins.RetrieveModelMixin,
#         mixins.UpdateModelMixin,
#         mixins.DestroyModelMixin,
#         mixins.ListModelMixin,
#         mixins.CreateModelMixin,
#         viewsets.GenericViewSet):
class ProjectViewSet(viewsets.ModelViewSet):
    serializer_class = serializers.ProjectModelSerializer2
    queryset = Project.objects.all()
    search_fields = ['name', 'leader', 'desc', 'interfaces__name']

    """ search_fields 搜索字段模糊匹配設置
        # 給drf框架搜索引擎支持搜索的字段,見settings.py文件中的REST_FRAMEWORK全局設置
        # http://localhost/projects/?search=100
        進行模糊匹配,支持查詢name字段中包含100,leader字段中包含100,desc字段中包含100,項目所關聯的接口名稱中包含100的內容全部搜索出來
        如果需求中有了變更,需要支持其他字段的搜索,那麼就在這個search_fields列表中增加該字段即可,
        同理不需要搜索某字段,直接刪除該字段即可,實現了配置的可插拔操作。
        # interfaces__name關聯字段,當前項目關聯的接口名稱,interfaces項目的models.py文件中Interfaces模型類中,
        # projects字段設置的關聯字段設置的是related_name='interfaces',所以項目表中通過interfaces__name 就能查詢到所有相關的接口名稱

         9.實現搜索過濾功能
             》指定過濾引擎,有兩種方式
                 方式一:在全局settings.py中指定DEFAULT_FILTER_BACKENDS(列表,指定過濾引擎的絕對路徑字符串)
                 方式二:在類視圖中filter_backends類屬性(列表,指定過濾引擎的引用),優先級高於全局
             》必須指定search_fields類屬性,定義待校驗的字段(模型類中的字段名字符串),默認忽略大小寫的包含過濾
                 '^': 'istartswith',
                 '=': 'iexact',
                 '@': 'search',
                 '$': 'iregex',
             》如果不指定search_fields類屬性,不會進行搜索過濾
             》調用視圖的filter_queryset()方法,對查詢集進行過濾,需要接收queryset查詢集參數
             》在全局settings.py中使用SEARCH_PARAM參數指定前端過濾查詢參數(默認為search)

    """
    # 過濾搜索和過濾排序設置,filter_backends 搜索過濾設置,使用drf框架中filters的SearchFilter和OrderingFilter類進行實現
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    filter_fields = ['name', 'desc', 'is_execute']
    # ordering_fields 排序字段設置
    ordering_fields = ['name', 'id', 'leader']
    # 在settings.py文件中'ORDERING_PARAM': 'ordering'是給drf框架的排序引擎設置的參數,
    # ordering_fields允許用户指定排序字段'name', 'id', 'leader'
    # 這個是相關的請求方法示例,http:/localhost/projects/?page=2&page_size=3&ordering=-id
    """ filter_fields特定字段精確匹配過濾設置,
        # 請求示例:http://localhost/projects/?name=zhangsanfeng2222
        # filter_backends 過濾搜索和過濾排序設置,使用drf框架中filters的SearchFilter和OrderingFilter類進行實現

         10.實現排序過濾功能
             》指定過濾引擎,有兩種方式
                 方式一:在全局settings.py中指定DEFAULT_FILTER_BACKENDS(列表,指定過濾引擎的絕對路徑字符串)
                 方式二:在類視圖中filter_backends類屬性(列表,指定過濾引擎的引用),優先級高於全局
             》必須指定ordering_fields類屬性,定義支持排序的字段(模型類中的字段名字符串)
             》如果不指定ordering_fields類屬性,那麼支持由 serializer_class 屬性指定的序列化器上的任何可讀字段進行排序
             》指定ordering_fields = '__all__',指定視圖應允許對模型的所有字段進行排序
             》指定ordering類屬性,定義默認的排序字段;也可以 Projects.objects.all().order_by('name')
             》調用視圖的filter_queryset()方法,對查詢集進行過濾,需要接收queryset查詢集參數
             》在全局settings.py中使用ORDERING_PARAM參數指定前端過濾查詢參數(默認為ordering)

    """
    # filterset_class = ProjectFilter
    # filterset_class自定義的過濾器類,因django-filter和當前的python以及drf版本不一致,無法使用該插件,
    # 雖然可以實現各種各樣複雜的過濾規則,但是也不太推薦了,查詢出來的結果可以讓前端人員進行查詢渲染
    pagination_class = PageNumberPagination
    """ pagination_class分頁過濾設置

         11.實現分頁功能
             》指定分頁引擎類,有兩種方式
                 方式一:在全局settings.py中指定DEFAULT_PAGINATION_CLASS(指定分頁引擎的絕對路徑字符串)
                 方式二:在類視圖中pagination_class類屬性(指定分頁引擎的引用),優先級高於全局
             》必須在settings.py中DEFAULT_PAGINATION_CLASS指定PAGE_SIZE參數(指定默認每一頁顯示的數據條數)或者
                 在自定義分頁引擎類指定page_size參數,如果未指定,那麼分頁功能不會開啓
             》在獲取列表數據實例方法中調用paginate_queryset方法,需要接收queryset查詢集對象,會返回嵌套page對象的列表
             》調用get_paginated_response方法將分頁數據返回,需要接收serializer.data參數
             》如果需要開啓前端能夠指定獲取每一頁的數據條數,往往需要重寫分頁引擎類PageNumberPagination
                 》page_size指定默認每一頁數據條數
                 》page_query_param設置前端指定頁碼的查詢字符串參數名稱
                 》page_query_description,對前端指定頁碼的查詢字符串參數的中文描述
                 》必須重寫page_size_query_param類屬性,才能開啓前端能夠指定獲取每一頁的數據條數的功能
                 》page_size_query_description是page_size_query_param類屬性的中文描述
                 》max_page_size指定前端每一頁數據最大條數的最大值
             》如果需要定製分頁數據的返回,那麼就需要重寫get_paginated_response方法

    """


    # def retrieve(self, request, *args, **kwargs):
    #     instance = self.get_object()
    #     serializer = self.get_serializer(instance=instance)
    #     return Response(serializer.data, status=status.HTTP_200_OK)
    #
    # def update(self, request, *args, **kwargs):
    #     serializer = self.get_serializer(data=request.data, instance=self.get_object())
    #     serializer.is_valid(raise_exception=True)
    #     serializer.save()
    #     return Response(serializer.data, status=status.HTTP_201_CREATED)
    #
    # def destory(self, request, *args, **kwargs):
    #     instance = self.get_object()
    #     instance.delete()
    #     return Response(None, status=status.HTTP_204_NO_CONTENT)
    #
    # 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(instance=page, many=True)
    #         return self.get_paginated_response(serializer.data)
    #
    #     serializer = self.get_serializer(instance=queryset, many=True)
    #     return Response(data=serializer.data, status=status.HTTP_200_OK)
    #
    # def create(self, request, *args, **kwargs):
    #     serializer = self.get_serializer(data=request.data)
    #     serializer.is_valid(raise_exception=True)
    #     serializer.save()
    #     return Response(data=serializer.data, status=status.HTTP_201_CREATED)

    def name_detail(self, request, *args, **kwargs):
        instance = self.get_object()
        return Response({
            'id': instance.id,
            'name': instance.name
        })

    def names(self, request, *args, **kwargs):
        # queryset = self.get_queryset()
        # 1.如果設置的是self.get_queryset()獲取的查詢集,就不支持上面的過濾和排序功能
        # http://localhost/projects/names_list/?search=11&ordering=id 進行請求時,後面的排序和過濾不起作用

        queryset = self.filter_queryset(self.get_queryset())
        # 2.如果設置的是self.filter_queryset(self.get_queryset())調用filter_queryset進行過濾,獲取的查詢集,
        # http://localhost/projects/names_list/?search=11&ordering=id 就支持設置的過濾和排序功能
        names = [obj.name for obj in queryset]
        return Response(names)