必知必會的重要知識點

settings.py文件內容
"""
Django settings for dev16 project.

Generated by 'django-admin startproject' using Django 5.2.6.

For more information on this file, see
https://docs.djangoproject.com/en/5.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""

from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-%a_&l*d94^%d+fqnzw72t3@3=6ml54-6-zev!e@n9r8ena0102'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    # 'django_filters',
    'projects',
    'interfaces',
    'projmod',
    'items',
    'kefan',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'dev16.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'dev16.wsgi.application'


# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases

DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        # 'NAME': BASE_DIR / 'db.sqlite3',
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'dev16day919',
        'USER': 'root',
        'PASSWORD': 'eisoo.com123',
        'PORT': 3306
    }
}


# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

REST_FRAMEWORK = {
    # 'DEFAULT_PARSER_CLASSES': [
    #     'rest_framework.parsers.JSONParser',
    #     'JSONParserrest_framework.parsers.FormParser',
    #     'rest_framework.parsers.MultiPartParser'
    # ],
    #
    # 'DEFAULT_RENDERER_CLASSES': [
    #     'rest_framework.renderers.JSONRenderer',
    #     'rest_framework.renderers.BrowsableAPIRenderer',
    # ],

    'DEFAULT_FILTER_BACKENDS': [
        'rest_framework.filters.SearchFilter',  # drf框架的過濾引擎
        'rest_framework.filters.OrderingFilter',    # drf框架的排序引擎
        # 'django_filters.rest_framework.backends.DjangoFilterBackend',   #
    ],

    'SEARCH_PARAM': 'search',   # 給drf框架的搜索引擎設置的參數
    'ORDERING_PARAM': 'ordering',   # 給drf框架的排序引擎設置的參數http:/localhost/projects/?page=2&page_size=3&ordering=-id

    # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'DEFAULT_PAGINATION_CLASS': 'utils.pagination.PageNumberPagination',    # drf框架的分頁引擎
    'PAGE_SIZE': 3, # 給drf框架的分頁引擎設置的每一頁有幾條數據,這個是必須要指定的
}




urls.py文件內容

# from django.urls import path, re_path, include
# from . import views
#
# urlpatterns = [
#     path('projects/<int:pk>/', views.ProjectDetailView.as_view()),
#     path('projects/', views.ProjectView.as_view()),
# ]
#
# from django.urls import path, re_path, include
# from . import views
# app_name = 'projects'
# urlpatterns = [
#     path('projects/<int:pk>/', views.ProjectViewSet.as_view({
#         'get': 'retrieve',  # 單查
#         'put': 'update',    # 單個更新
#         'delete': 'destroy',    # 單個刪除
#     })),
#     path('projects/', views.ProjectViewSet.as_view({
#         'get': 'list',  # 多查,查詢集合
#         'post': 'create',   # 創建,反序列化入庫
#     })),
#     path('projects/<int:pk>/name_detail/', views.ProjectViewSet.as_view({
#         'get': 'name_detail',   # 查詢指定項目的名稱詳情
#     })),
#     path('projects/names/', views.ProjectViewSet.as_view({
#         'get': 'names',     # 查詢項目的所有名稱集合
#     })),
# ]

# from django.urls import path, re_path, include
# from . import views
# urlpatterns = [
#     path('projects/<int:pk>/', views.ProjectViewSet.as_view({
#         'get': 'retrieve',  # 單查
#         'put': 'update',    # 單個更新
#         'delete': 'destroy' # 單個刪除
#     })),
#     path('projects/', views.ProjectViewSet.as_view({
#         # 羣查
#         'get': 'list',
#         # 創建
#         'post': 'create',
#     })),
#     path('projects/<int:pk>/name_detail/', views.ProjectViewSet.as_view({
#         # 查看單個項目的名稱詳情
#         'get': 'name_detail',
#     })),
#     path('projects/names/', views.ProjectViewSet.as_view({
#         # 羣查所有的項目名稱
#         'get': 'names',
#     })),
# ]

# from django.urls import path, re_path, include
# from . import views
#
# urlpatterns = [
#     path('projects/<int:pk>/', views.ProjectViewSet6.as_view({
#         'get': 'retrieve',
#         'put': 'update',
#         'delete': 'destroy'
#     })),
#     path('projects/', views.ProjectViewSet6.as_view({
#         'get': 'list',
#         'post': 'create'
#     })),
#     path('projects/<int:pk>/name_details/', views.ProjectViewSet6.as_view({
#         'get': 'name_details',
#     })),
#     path('projects/names/', views.ProjectViewSet6.as_view({
#         'get': 'names',
#     }))
# ]
# 需求:實現項目表的單查、單個更新、單個刪除、多查、羣增、單查PK的name詳情、羣查所有項目的名稱信息
from django.urls import path, re_path, include
from . import views

# urlpatterns = [
#     path('projects/<int:pk>/', views.ProjectViewSet7.as_view({
#         'get': 'retrieve',
#         'put': 'update',
#         'delete': 'destroy',
#     })),
#     path('projects/', views.ProjectViewSet7.as_view({
#         'post': 'create',
#         'get': 'list',
#     })),
#     path('projects/<int:pk>/name_detail/', views.ProjectViewSet7.as_view({
#         'get': 'name_detail',
#     })),
#     path('projects/names_list/', views.ProjectViewSet7.as_view({
#         'get': 'names',
#     }))
# ]


"""
# 路由器設計:自動生成路由
1.admin/
2.^projects/$ [name='proje-list']
3.^projects/nameslist/$ [name='proje-nameslist']
4.^projects/(?P<pk>[^/.]+)/$ [name='proje-detail']
5.^projects/(?P<pk>[^/.]+)/details/$ [name='proje-details']
"""
from rest_framework import routers
app_name = 'projects'
router = routers.SimpleRouter()
# router = routers.DefaultRouter()
router.register(r'projects', views.ProjectViewSet7, basename='proje')
urlpatterns = [

]

urlpatterns += router.urls


models.py文件內容:
from django.db import models
from utils.base_model import BaseModel


# Create your models here.
"""
1.模型類必須繼承Model或者Model的子類,
2.一個ORM模型類就對應了一個數據庫中的一張表
3.在ORM模型類中定義類屬性,並且類屬性必須是Field的子類,與數據表中的字段對應
4.CharField與mysql中的varchar對應,並且必須得指定max_length參數,指定當前字段的最大長度
5.IntegerField類與mysql中的int對應,都是整形
6.ORM模型類會自動創建一個名為id的自增主鍵(非空且唯一),為int類型
7.生成遷移腳本
    a.python manage.py makemigrations 子應用名稱
    b.如果不指定子應用名稱,那麼會將所有子應用包括內置的子應用根據models.py文件中的字段內容生成遷移腳本
8.生成遷移腳本,並不會創建表,只有執行遷移腳本之後,才會創建表
    a.python manage.py migrate 子應用名稱
    b.如果不指定子應用名稱,那麼會執行所有子應用(包括內置)的migrations包中的遷移腳本
9.生成表的名稱默認為子應用名_模型類名小寫
10.打印遷移腳本的SQL語句,python manage.py sqlmigrate 子應用名稱   遷移腳本名(不包括後綴的.py,用前面的幾個字母就可以)
----------------------------------------------------------------------------------------------------------
a.如果ORM模型類中某個字段指定了primary_key=True, 那麼ORM框架就不會自動生成名稱為id的自增主鍵
b.會把制定了primary_key=True的字段作為主鍵
c.創建的ORM模型類中字段默認primary_key=False,為非主鍵字段
d.verbose_name主要用於提供字段的人類可讀名稱,改善字段的顯示效果
e.help_text則用於提供字段的額外説明或者幫助信息,增加字段的可理解性
    總的來説,這兩個字段都是為了提高模型字段在管理後台和表單中的可用性和友好性。
    verbose_name關注於如何命名字段,而help_text關注於如何解釋字段
f.使用unique=True,為當前字段指定唯一約束,默認創建的ORM模型類字段unique=False,為False表示可以重複
g.使用null=True,表示數據庫中可以為null,指定當前字段在數據庫中是否可以存儲NULL值,默認該字段在數據庫中必須有一個值
h.使用default=True,為當前字段指定默認值,指定默認值以後,前端創建數據時,如果不指定該字段內容,那麼會自動將默認值作為當前字段的值。
i.使用blank=True, 指定前端在創建數據時,可以不用傳遞該字段,表單驗證中,表單可以不填寫內容,
    如果blank=False, 則該字段在表單驗證時必須要有一個值。
j.DateTimeField、DateField字段添加auto_now_add和auto_now參數
    1.auto_now_add=True 指定在創建該記錄時,會自動將當前創建的時間作為該字段的值,後續不會發生變更
    2.auto_now=True 在每次更新記錄時,會自動將當前更新的時間作為最新的值,每次變更都會自動修改
    3.auto_now_add和auto_now不能同時指定

"""
class Kefan(BaseModel):
    name = models.CharField(max_length=50, verbose_name='項目名稱', help_text='請輸入項目名稱')
    leader = models.CharField(max_length=50, verbose_name='項目負責人', help_text='請輸入項目負責人')
    is_execute = models.BooleanField(default=False, verbose_name='是否執行項目', help_text='請輸入是否執行項目')
    desc = models.TextField(verbose_name='項目簡介', help_text='請輸入項目簡介', null=True, blank=True)
    """
    null=True, 指定當前字段在數據庫中是否可以存儲NULL值,默認該字段在數據庫中必須有一個值。
    blank=True, blank空白的,指定前端在創建數據時,可以為空,
    blank=False, 指定前端在創建數據時,不能為空。
    """
    class Meta:
        db_table = 'tb_kefan'
        verbose_name = '科範項目表'
        verbose_name_plural = verbose_name


serializers.py文件內容:
# from rest_framework import serializers
# from . import models
#
#
# def validate_project_name(value):
#     if '項目' not in value:
#         raise serializers.ValidationError('項目名稱中必須包含<項目>關鍵詞')
#
#
# class ProjectModelSerializer(serializers.ModelSerializer):
#     """
#     序列化器類和模型類的關係?
#     1.這兩個類高度相似
#     2.當模型類中字段非常多時,定義序列化器類相當麻煩
#     3.可以定義模型序列化器,來自動將模型類中的字段生成序列化器中的字段
#         必須繼承serializers.ModelSerializer父類
#         必須在Meta內部類中,使用fields指定模型類中哪些字段需要生成序列化器字段
#         必須在Meta內部類中,使用model類屬性關聯一個模型類
#         如果指定fields='__all__', 那麼會將模型類中的所有字段生成序列化器字段
#         在生成的字段中,會將主鍵id設置為IntegerField類型,同時會自動指定read_only=True
#         會將DateTimeField類型中有添加auto_now_add或者auto_now參數,會自動指定read_only=True
#         會將unique=True的字段,自動生成validators=UniqueValidator唯一約束
#         如果只需要將模型類中某些字段生成序列化器字段,可以將這些字段組成元組或者列表,傳遞給fields
#         可以在模型序列化器類中定義模型類中的字段,會覆蓋自動生成的字段
#         fields類屬性,如果指定的説元組類型,那麼必須包含所有的序列化器字段,
#         如果fields為__all__或者exclude那麼無需指定模型類外的字段
#         模型序列化器類自帶create和update方法,一般無需重寫
#         可以在Meta內部類中的extra_kwargs類屬性中對模型類中的自動生成的字段進行修改,將模型類字段名作為key,把具體要修改的參數作為字典的值
#     4.不同方式定義的序列化器對象的區別
#         1.如果在創建序列化器對象時,僅僅只傳遞instance參數,那麼當前只會進行序列化輸出操作,不可能進行反序列化輸入操作
#             不可以調用序列化器對象的save方法
#         2.如果在創建序列化器對象時,僅僅只傳遞data參數,那麼在調用is_valid方法後會進行反序列化輸入操作,
#             同時在調用save方法時,會自動調用序列化器對象的create方法,用於數據的創建操作,會把create方法返回的數據作為序列化輸出的數據源
#         3.如果在創建序列化器對象時,僅僅只傳遞data參數,調用is_valid方法對數據進行校驗之後,直接調用serializer.data屬性,未調用save方法,
#             會把校驗通過的數據validated_data作為序列化輸出的數據源,如果校驗通過的數據中有相應的數據,同時該字段需要輸出,那麼輸出的數據中才會有該字段
#         4.如果在創建序列化器對象時,同時傳遞instance和data參數,調用is_valid方法,對數據進行校驗,那麼在調用save方法時,
#             會自動調用序列化器對象的update方法,用於數據的更新操作,會把update方法返回的數據作為序列化輸出的數據源。
#     """
#     # 需求1. 需要定義一些在模型類中沒有的字段,僅僅只進行反序列化輸入操作,例如:確認密碼
#     xxx = serializers.CharField(write_only=True)
#     # 需求2. 需要定義一些在模型類中沒有的字段,僅僅只進行序列化輸出操作,例如:token的值
#     token = serializers.CharField(read_only=True)
#
#     class Meta:
#         model = models.Project
#         fields = ['id', 'name', 'leader', 'is_execute', 'xxx', 'token']
#         extra_kwargs = {
#             'name': {
#                 'min_length': 2,
#                 'label': '新的label',
#                 'validators': [validate_project_name]
#             }
#         }
#
#     def validate(self, attrs: dict):
#         var = attrs.get('xxx')
#         attrs.pop('xxx')
#         return attrs
#
#     def create(self, validated_data: dict):
#         instance = super().create(validated_data)
#         instance.token = 'xxxx dddd ffff gggg'
#         return instance
#
#
# class ProjectModelSerializer1(serializers.ModelSerializer):
#     class Meta:
#         model = models.Project
#         fields = '__all__'
#
#
# class ProjectModelSerializer2(serializers.ModelSerializer):
#     class Meta:
#         model = models.Project
#         fields = ['id', 'name', 'leader', 'desc']

# from rest_framework import serializers
# from . import models
#
#
# def validate_project_name(value):
#     if '項目' not in value:
#         raise serializers.ValidationError('項目名稱中必須包含<項目>關鍵詞')
#
#
# class ProjectModelSerializer(serializers.ModelSerializer):
#     # confirm_password字段進行反序列化輸入
#     confirm_password = serializers.CharField(write_only=True)
#     password = serializers.CharField(write_only=True)
#     # token字段進行序列化輸出
#     token = serializers.CharField(read_only=True)
#
#     class Meta:
#         model = models.Project
#         fields = '__all__'
#
#     def validate(self, attrs: dict) -> dict:
#         # 反序列化輸入的字段中刪除confirm_password字段
#         var = attrs.get('confirm_password')
#         # 邏輯1,對confirm_password進行校驗和初始密碼是否一致
#         if var != attrs['password']:
#             raise serializers.ValidationError('用户輸入的確認密碼和原始密碼不一致')
#         # 邏輯2,刪除確認密碼,不讓該字段進入到數據庫中
#         attrs.pop('confirm_password')
#         return attrs
#
#     def create(self, validated_data):
#         # validated_data 是序列化器校驗通過的數據
#         instance = super().create(validated_data)
#         instance.token = 'Token Ludundun XXX'
#         return instance
#
#
# class ProjectModelSerializer1(serializers.ModelSerializer):
#     class Meta:
#         model = models.Project
#         fields = '__all__'
#
#
# class ProjectModelSerializer2(serializers.ModelSerializer):
#     class Meta:
#         model = models.Project
#         fields = ['id', 'name', 'leader']

from rest_framework import serializers

from interfaces.models import Interfaces
from . import models


def validate_project_name(value):
    if '項目' not in value:
        raise serializers.ValidationError('項目名稱中必須包含<項目>關鍵詞')


class ProjectModelSerializer(serializers.ModelSerializer):
    # confirm_password字段進行反序列化輸入
    confirm_password = serializers.CharField(write_only=True)
    password = serializers.CharField(write_only=True)
    # token字段進行序列化輸出
    token = serializers.CharField(read_only=True)

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

    def validate(self, attrs: dict):
        # 反序列化輸入的字段中刪除confirm_password字段
        var = attrs.get('confirm_password')
        # 邏輯1,對confirm_password進行校驗和初始密碼是否一致
        if var != attrs['password']:
            raise serializers.ValidationError('用户輸入的確認密碼和原始密碼不一致')
        # 邏輯2,刪除確認密碼,不讓該字段進入到數據庫中
        attrs.pop('confirm_password')
        return attrs

    def create(self, validated_data):
        # validated_data 是序列化器校驗通過的數據
        instance = super().create(validated_data)
        instance.token = 'Token Ludundun xxxxxx'
        return instance


class ProjectModelSerializer2(serializers.ModelSerializer):
    class Meta:
        model = models.Project
        fields = ['id', 'name', 'leader']


class InterfaceModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Interfaces
        fields = ['id', 'name', 'create_time']


class ProjectModelSerializer1(serializers.ModelSerializer):
    interfaces = InterfaceModelSerializer(read_only=True, many=True)  # 設置read_only=True只支持序列化輸出

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

views.py視圖文件內容:

from rest_framework.generics import GenericAPIView
from rest_framework.request import Request
from rest_framework import filters
from rest_framework import permissions
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 rest_framework.decorators import action
from . import serializers
# from .filters import ProjectFilter
from utils.pagination import PageNumberPagination

"""

# 源碼中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, 繼承CreateModelMixin、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方法進行一一對應功能,以及認證授權限流功能,
    GenericAPIView提供了get_object()、get_queryset()、get_serializer()等方法
3.GenericViewSet繼承了ViewSetMixin、GenericAPIView,具備請求方法與要調用的action方法進行一一對應功能,以及支持Mixin擴展類
4.ReadOnlyModelViewSet繼承了RetrieveModelMixin、ListModelMixin、GenericViewSet,提供了兩個讀取數據的接口
5.ModelViewSet繼承了CreateModelMixin、RetrieveModelMixin、UpdateModelMixin、
    DestroyModelMixin、ListModelMixin、GenericViewSet,提供了6個接口。
6.ViewSetMixin類提供了請求方法與要調用的action方法進行一一對應功能,在定義路由條目時,在as_view()方法中支持接收字典數據,
    把請求方法名稱字符串作為key, 把具體要調用的action方法名字符串作為值。


drf中的自動生成路由:
1.自動生成路由條目,默默僅僅只會生成通用的action,自定義的action默認不會生成
2.需要使用action裝飾器,from rest_framework.decorators import action裝飾自定義的action方法,那麼才支持自動生成路由條目,
    》第一個參數為methods指定當前action支持的請求方法,為列表類型,將當前已支持的action方法作為元素retrieve、update、create、destroy、list、partrail_update
    》如果不進行設置,默認methods=['get']
    》第二個參數必須是detail關鍵字參數,指定是否需要接收主鍵ID,其實顧名思義就是是否獲取詳情信息
        detail為False,無需接收主鍵id;detail為True,需要接收主鍵ID。
    》url_path指定生成路由條目時路徑名稱,默認當前action方法名稱
    》url_name指定生成的路由條目名稱後綴,默認為當前action方法名稱
3.生成路由條目的操作方式
from rest_framework import routers
router = routers.SimpleRouter()
# router = routers.DefaultRouter()
router.register(r'projects', views.ProjectViewSet7, basename='proje')
    a.定義SimpleRouter路由器對象
    b.調用路由器對象的register方法,對路由進行註冊
        》第一個參數需要設置r'路由前綴'
        》第二個參數需要指定視圖集類引用,不能調用as_view()方法
        》basename參數指定生成的路由條目名稱前綴,如果不指定,那麼默認生成的路由條目名稱前綴為第一個參數指定的值
    c.使用路由器對象.urls屬性,能獲取自動生成的路由條目

類視圖的設計原則(有則用父類,無則自定義,半有半無就重寫父類)
這句話啥意思呢,就是繼承父類,父類中有的方法,就直接用父類的就行,如果父類也沒有該方法,就需要自己自定義該方法了,
或者父類中有這個相關的方法,但是不夠全面,那就需要你自己再重新寫一些父類中的這個方法進行完善了
1.如果父類有提供相應的資源,Mixin擴展類、具體通用的視圖,並且能完全滿足需求,就直接使用父類提供的
2.如果父類提供的資源僅僅只是部分不滿足需求,重寫父類的實現
3.如果父類提供的資源完全不滿足需求,直接自定義
4.按需要選擇父類
5.默認情況下,直接使用ModelViewSet

序列化器類的設計原則:
1.如果操作的是模型數據,一定使用ModelSerializer,生成的字段不滿足需求,可修改
2.如果操作的是普通對象,那麼才使用Serializer
3.複雜的查詢邏輯,一般把其他序列化器類作為序列化器的字段進行使用,如下所示,進行序列化輸出時,會把InterfaceModelSerializer的fields信息,
作為ProjectModelSerializer1序列化器類序列化的字段內容輸出到前端:
{
class InterfaceModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = Interfaces
        fields = ['id', 'name', 'create_time']


class ProjectModelSerializer1(serializers.ModelSerializer):
    interfaces = InterfaceSerializer(read_only=True, many=True)

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

}


"""


class ProjectDetailView(generics.RetrieveUpdateDestroyAPIView):
    # 支持單查、更新、刪除數據
    serializer_class = serializers.ProjectModelSerializer2
    queryset = Project.objects.all()


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']


class ProjectViewSet(viewsets.ModelViewSet):
    serializer_class = serializers.ProjectModelSerializer2
    queryset = Project.objects.all()
    search_fields = ['name', 'leader', 'desc', 'interfaces__name']
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    pagination_class = PageNumberPagination
    ordering_fields = ['name', 'id', 'leader']

    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()
        names = [obj.name for obj in queryset]
        return Response(data=names, status=status.HTTP_200_OK)


class ProjectViewSet1(viewsets.ModelViewSet):
    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 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()
        data = [obj.name for obj in queryset]
        return Response(data)


class ProjectViewSet2(viewsets.ModelViewSet):
    serializer_class = serializers.ProjectModelSerializer2
    queryset = Project.objects.all()
    search_fields = ['name', 'leader', 'desc', 'interfaces__name']
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    pagination_class = PageNumberPagination
    ordering_fields = ['name', 'id', 'leader']

    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()
        data = [obj.name for obj in queryset]
        return Response(data)


class ProjectViewSet3(viewsets.ModelViewSet):
    serializer_class = serializers.ProjectModelSerializer2
    queryset = Project.objects.all()
    search_fields = ['name', 'leader']
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    pagination_class = PageNumberPagination
    ordering_fields = ['name', 'id', 'leader']

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

    def names(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        data = [obj.name for obj in queryset]
        return Response(data)


from rest_framework import viewsets
from .models import Project


class ProjectViewSet6(viewsets.ModelViewSet):
    serializer_class = serializers.ProjectModelSerializer
    queryset = Project.objects.all()
    search_fields = ['name', 'leader']
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    pagination_class = PageNumberPagination
    ordering_fields = ['name', 'id', 'leader']

    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()
        names = [obj.name for obj in queryset]
        return Response(names)


class ProjectViewSet7(viewsets.ModelViewSet):
    serializer_class = serializers.ProjectModelSerializer
    # queryset = Project.objects.all().order_by('id')     # 設置默認id排序
    queryset = Project.objects.all()
    search_fields = ['name', 'leader']
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    pagination_class = PageNumberPagination
    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
    """
    使用router自動生成路由條目,對於非默認的action不能生成路由,之能對默認的action生成路由條目(retrieve、update、destroy、list、create、partrial_update)
    非默認的action,例如下面的name_detail和names,需要添加action裝飾器進行裝飾,裝飾完以後,才能實現自動添加到路由中,
    @action(methods=['get'], detail=True, url_path='details', url_name='details')
        1.裝飾器中method不是必傳參數,method必須是列表,
    默認是get,可以為其添加多個方法,get、post等都支持。
        2.detail是必傳參數,detail=True,表示獲取的是詳情信息,url中需要傳PK信息,相當於單查;
    detail=False,url中不需要傳PK信息,相當於羣查。
        3.url_path參數默認值是被裝飾的函數名稱,表示路徑中用到的是該函數的名稱,也可以修改為其他值
        4.url_name參數使用的值是自動生成的路由路徑最後面proje後的值,默認可以不設置該參數。
        http://localhost:8000/projects/(?P<pk>[^/.]+)/details/$ [name='proje-details']
        router.register(r'projects', views.ProjectViewSet7, basename='proje')
        自動路由器設置的basename='proje'參數,會映射到上面的url路徑[name='proje-details']最後面的參數中。
    
    """
    @action(methods=['get'], detail=True, url_path='details', url_name='details')
    # 設置action信息以後,請求的url如下所示
    # http://localhost:8000/projects/(?P<pk>[^/.]+)/details/$ [name='proje-details']
    def name_detail(self, request, *args, **kwargs):
        instance = self.get_object()
        return Response({
            'id': instance.id,
            'name': instance.name
        })

    @action(methods=['get'], detail=False, url_path='nameslist', url_name='nameslist')
    # 設置action信息以後,請求的url如下所示
    # http://localhost:8000/projects/nameslist/$ [name='proje-nameslist']
    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)


# 設置該視圖類不支持刪除方法
class ProjectViewSet8(mixins.ListModelMixin,
                     mixins.CreateModelMixin,
                     mixins.RetrieveModelMixin,
                     mixins.UpdateModelMixin,
                     viewsets.GenericViewSet):
    serializer_class = serializers.ProjectModelSerializer
    queryset = Project.objects.all()
    search_fields = ['name', 'leader', 'desc', 'interfaces__name']
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    pagination_class = PageNumberPagination
    ordering_fields = ['name', 'id', 'leader']
    # 設置訪問權限
    permission_classes = [permissions.IsAuthenticated]
    # permissions.IsAdminUser
    # permissions.IsAuthenticatedOrReadOnly

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

    @action(methods=['get'], detail=True, url_path='names', url_name='names')
    def name_detail(self, request, *args, **kwargs):
        instance = self.get_object()
        return Response({
            'id': instance.id,
            'name': instance.name
        })

    @action(detail=False, url_path='names', url_name='names')
    def names(self, request, *args, **kwargs):
        # 第一次改造
        # 1.過濾
        # queryset = self.filter_queryset(self.get_queryset())
        # queryset = self.get_queryset()
        # 2.序列化輸出
        # data = [ obj.name for obj in queryset]
        # return Response(data)
        # serializer = self.get_serializer(instance=queryset, many=True)
        # return Response(serializer.data)

        # 第二次改造
        # return self.list(request, *args, **kwargs)

        # 第四次改造
        response: Response = self.list(request, *args, **kwargs)
        data_lst = [d.get('name') for d in response.data]
        response.data = data_lst
        return response

    # 第三次改造
    def paginate_queryset(self, queryset):
        if self.action == 'names':
            return None
        else:
            return super().paginate_queryset(queryset)

    def get_serializer_class(self):
        if self.action == 'names':
            return serializers.ProjectNameListModelSerializer
        else:
            return super().get_serializer_class()

    def filter_queryset(self, queryset):
        if self.action == 'names':
            return queryset
        else:
            return super().filter_queryset(queryset)