使用Python設計與實現(xiàn)一個文件夾內(nèi)容查看器
完整代碼實現(xiàn)
# -*- coding: utf-8 -*-
"""文件夾內(nèi)容查看器 - 完整優(yōu)化版"""
import os
import sys
import time
from pathlib import Path
from typing import List, Dict, Optional, Tuple
from datetime import datetime
from enum import Enum
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLineEdit, QProgressBar, QLabel, QMessageBox,
QFileDialog, QTextEdit, QSplitter, QTreeWidget, QTreeWidgetItem,
QHeaderView, QMenu, QAction, QSizePolicy, QComboBox,
QCheckBox, QGroupBox, QToolBar, QStatusBar, QFrame)
from PyQt5.QtCore import Qt, pyqtSignal, QThread, QTimer, QSize
from PyQt5.QtGui import QFont, QIcon, QColor, QPalette
class SortMethod(Enum):
"""排序方法枚舉"""
NAME_ASC = "名稱 (A-Z)"
NAME_DESC = "名稱 (Z-A)"
SIZE_ASC = "大小 (小→大)"
SIZE_DESC = "大小 (大→小)"
DATE_ASC = "日期 (舊→新)"
DATE_DESC = "日期 (新→舊)"
TYPE_ASC = "類型 (A-Z)"
class FileSystemScanner(QThread):
"""文件系統(tǒng)掃描線程"""
progress_updated = pyqtSignal(int, str, int, int) # 進度, 當前文件, 文件夾數(shù), 文件數(shù)
finished_signal = pyqtSignal(list, int, int, int) # 文件列表, 文件夾數(shù), 文件數(shù), 總大小
error_signal = pyqtSignal(str)
directory_loaded = pyqtSignal(str)
def __init__(self, folder_path: str, include_hidden: bool = False,
show_file_size: bool = True, max_items: int = 10000):
super().__init__()
self.folder_path = folder_path
self.include_hidden = include_hidden
self.show_file_size = show_file_size
self.max_items = max_items
self._is_cancelled = False
def cancel(self):
"""取消掃描"""
self._is_cancelled = True
def is_hidden(self, path: Path) -> bool:
"""檢查文件是否隱藏"""
try:
return path.name.startswith('.') or os.stat(path).st_file_attributes & 2
except:
return False
def format_file_size(self, size_bytes: int) -> str:
"""格式化文件大小顯示"""
if size_bytes == 0:
return "0 B"
size_units = ["B", "KB", "MB", "GB", "TB"]
unit_index = 0
size = float(size_bytes)
while size >= 1024 and unit_index < len(size_units) - 1:
size /= 1024
unit_index += 1
return f"{size:.2f} {size_units[unit_index]}"
def run(self):
"""執(zhí)行掃描"""
try:
folder = Path(self.folder_path)
if not folder.exists():
self.error_signal.emit("路徑不存在")
return
if not folder.is_dir():
self.error_signal.emit("選擇的不是文件夾")
return
# 獲取文件夾內(nèi)容
items = []
dir_count = 0
file_count = 0
total_size = 0
try:
all_items = list(folder.iterdir())
total_items = len(all_items)
for idx, item in enumerate(all_items):
if self._is_cancelled:
return
# 檢查隱藏文件
if not self.include_hidden and self.is_hidden(item):
continue
try:
is_dir = item.is_dir()
size = item.stat().st_size if not is_dir else 0
mod_time = datetime.fromtimestamp(item.stat().st_mtime)
file_info = {
'name': item.name,
'path': str(item),
'is_dir': is_dir,
'size': size,
'size_str': self.format_file_size(size) if self.show_file_size else "",
'mod_time': mod_time,
'extension': item.suffix.lower() if not is_dir else "文件夾"
}
items.append(file_info)
if is_dir:
dir_count += 1
else:
file_count += 1
total_size += size
# 發(fā)射進度信號
self.progress_updated.emit(
int((idx + 1) / total_items * 100),
item.name,
dir_count,
file_count
)
# 防止處理時間過長,偶爾讓出CPU
if idx % 100 == 0:
time.sleep(0.001)
except (PermissionError, OSError) as e:
continue
# 限制最大項目數(shù)
if len(items) >= self.max_items:
self.error_signal.emit(f"項目數(shù)量超過限制 ({self.max_items})")
break
except PermissionError:
self.error_signal.emit("沒有權限訪問該文件夾")
return
# 發(fā)射完成信號
self.finished_signal.emit(items, dir_count, file_count, total_size)
self.directory_loaded.emit(self.folder_path)
except Exception as e:
self.error_signal.emit(f"掃描過程中發(fā)生錯誤: {str(e)}")
class FolderContentViewer(QMainWindow):
"""文件夾內(nèi)容查看器主窗口"""
def __init__(self):
super().__init__()
self.scanner_thread = None
self.current_folder = ""
self.current_items = []
self.sort_method = SortMethod.NAME_ASC
self.include_hidden = False
self.show_file_size = True
self.init_ui()
self.setup_shortcuts()
def init_ui(self):
"""初始化用戶界面"""
self.setWindowTitle("文件夾內(nèi)容查看器")
self.setGeometry(100, 100, 1000, 700)
# 創(chuàng)建中央部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
# 主布局
main_layout = QVBoxLayout(central_widget)
main_layout.setSpacing(10)
main_layout.setContentsMargins(12, 12, 12, 12)
# 創(chuàng)建工具欄
self.create_toolbar()
# 文件夾選擇區(qū)域
self.create_folder_selection_area(main_layout)
# 統(tǒng)計信息區(qū)域
self.create_stats_area(main_layout)
# 進度條區(qū)域
self.create_progress_area(main_layout)
# 結果展示區(qū)域
self.create_results_area(main_layout)
# 狀態(tài)欄
self.create_status_bar()
# 設置樣式
self.apply_styles()
def create_toolbar(self):
"""創(chuàng)建工具欄"""
toolbar = QToolBar("主工具欄")
toolbar.setIconSize(QSize(16, 16))
self.addToolBar(toolbar)
# 刷新按鈕
refresh_action = QAction("刷新", self)
refresh_action.triggered.connect(self.refresh_content)
toolbar.addAction(refresh_action)
toolbar.addSeparator()
# 排序選擇
toolbar.addWidget(QLabel("排序:"))
self.sort_combo = QComboBox()
self.sort_combo.addItems([method.value for method in SortMethod])
self.sort_combo.currentTextChanged.connect(self.on_sort_changed)
toolbar.addWidget(self.sort_combo)
toolbar.addSeparator()
# 顯示選項
self.hidden_checkbox = QCheckBox("顯示隱藏文件")
self.hidden_checkbox.stateChanged.connect(self.on_show_hidden_changed)
toolbar.addWidget(self.hidden_checkbox)
self.size_checkbox = QCheckBox("顯示文件大小")
self.size_checkbox.setChecked(True)
self.size_checkbox.stateChanged.connect(self.on_show_size_changed)
toolbar.addWidget(self.size_checkbox)
def create_folder_selection_area(self, parent_layout):
"""創(chuàng)建文件夾選擇區(qū)域"""
folder_layout = QHBoxLayout()
self.select_btn = QPushButton("選擇文件夾")
self.select_btn.clicked.connect(self.select_folder)
self.select_btn.setMinimumWidth(100)
self.folder_path_input = QLineEdit()
self.folder_path_input.setPlaceholderText("請輸入文件夾路徑或點擊選擇按鈕...")
self.folder_path_input.returnPressed.connect(self.load_folder_from_input)
self.refresh_btn = QPushButton("刷新")
self.refresh_btn.clicked.connect(self.refresh_content)
self.refresh_btn.setMinimumWidth(80)
folder_layout.addWidget(self.select_btn)
folder_layout.addWidget(self.folder_path_input, 1)
folder_layout.addWidget(self.refresh_btn)
parent_layout.addLayout(folder_layout)
def create_stats_area(self, parent_layout):
"""創(chuàng)建統(tǒng)計信息區(qū)域"""
stats_layout = QHBoxLayout()
self.folder_count_label = QLabel("文件夾: 0")
self.file_count_label = QLabel("文件: 0")
self.total_count_label = QLabel("總計: 0")
self.total_size_label = QLabel("總大小: 0 B")
for label in [self.folder_count_label, self.file_count_label,
self.total_count_label, self.total_size_label]:
label.setAlignment(Qt.AlignCenter)
label.setMinimumWidth(120)
stats_layout.addWidget(label)
stats_layout.addStretch()
parent_layout.addLayout(stats_layout)
def create_progress_area(self, parent_layout):
"""創(chuàng)建進度條區(qū)域"""
progress_layout = QHBoxLayout()
self.progress_label = QLabel("就緒")
self.progress_bar = QProgressBar()
self.progress_bar.setTextVisible(True)
self.progress_bar.setAlignment(Qt.AlignCenter)
progress_layout.addWidget(self.progress_label)
progress_layout.addWidget(self.progress_bar, 1)
parent_layout.addLayout(progress_layout)
def create_results_area(self, parent_layout):
"""創(chuàng)建結果展示區(qū)域"""
# 創(chuàng)建分割器
splitter = QSplitter(Qt.Vertical)
# 樹形視圖
self.tree_widget = QTreeWidget()
self.tree_widget.setHeaderLabels(["名稱", "大小", "修改時間", "類型"])
self.tree_widget.header().setSectionResizeMode(0, QHeaderView.Stretch)
self.tree_widget.setAlternatingRowColors(True)
self.tree_widget.itemDoubleClicked.connect(self.on_item_double_clicked)
# 文本視圖
self.text_view = QTextEdit()
self.text_view.setReadOnly(True)
self.text_view.setPlaceholderText("文件夾內(nèi)容將顯示在這里...")
splitter.addWidget(self.tree_widget)
splitter.addWidget(self.text_view)
splitter.setSizes([400, 200])
parent_layout.addWidget(splitter, 1)
def create_status_bar(self):
"""創(chuàng)建狀態(tài)欄"""
self.statusBar().showMessage("就緒 - 請選擇要查看的文件夾")
def apply_styles(self):
"""應用樣式"""
self.setStyleSheet("""
QMainWindow {
background-color: #f0f0f0;
}
QPushButton {
padding: 5px 10px;
border: 1px solid #ccc;
border-radius: 3px;
background-color: #f8f8f8;
}
QPushButton:hover {
background-color: #e8e8e8;
}
QPushButton:pressed {
background-color: #d8d8d8;
}
QLineEdit {
padding: 5px;
border: 1px solid #ccc;
border-radius: 3px;
}
QProgressBar {
border: 1px solid #ccc;
border-radius: 3px;
text-align: center;
}
QProgressBar::chunk {
background-color: #4CAF50;
}
QTreeWidget {
alternate-background-color: #f8f8f8;
}
""")
def setup_shortcuts(self):
"""設置快捷鍵"""
self.refresh_btn.setShortcut("F5")
self.select_btn.setShortcut("Ctrl+O")
def select_folder(self):
"""選擇文件夾"""
folder_path = QFileDialog.getExistingDirectory(
self, "選擇文件夾",
self.current_folder or os.path.expanduser("~")
)
if folder_path:
self.folder_path_input.setText(folder_path)
self.load_folder(folder_path)
def load_folder_from_input(self):
"""從輸入框加載文件夾"""
folder_path = self.folder_path_input.text().strip()
if folder_path:
self.load_folder(folder_path)
def load_folder(self, folder_path: str):
"""加載文件夾內(nèi)容"""
if not folder_path:
return
folder = Path(folder_path)
if not folder.exists():
QMessageBox.warning(self, "警告", "文件夾不存在")
return
if not folder.is_dir():
QMessageBox.warning(self, "警告", "選擇的不是文件夾")
return
self.current_folder = folder_path
self.start_scanning()
def start_scanning(self):
"""開始掃描文件夾"""
# 停止現(xiàn)有的掃描線程
if self.scanner_thread and self.scanner_thread.isRunning():
self.scanner_thread.cancel()
self.scanner_thread.wait()
# 重置UI狀態(tài)
self.progress_bar.setValue(0)
self.progress_label.setText("正在掃描...")
self.set_controls_enabled(False)
self.statusBar().showMessage(f"正在掃描: {self.current_folder}")
# 創(chuàng)建并啟動掃描線程
self.scanner_thread = FileSystemScanner(
self.current_folder,
self.include_hidden,
self.show_file_size
)
self.scanner_thread.progress_updated.connect(self.update_progress)
self.scanner_thread.finished_signal.connect(self.on_scan_finished)
self.scanner_thread.error_signal.connect(self.on_scan_error)
self.scanner_thread.directory_loaded.connect(self.on_directory_loaded)
self.scanner_thread.start()
def update_progress(self, progress: int, current_file: str, dir_count: int, file_count: int):
"""更新進度顯示"""
self.progress_bar.setValue(progress)
self.progress_label.setText(f"處理中: {current_file}...")
self.folder_count_label.setText(f"文件夾: {dir_count}")
self.file_count_label.setText(f"文件: {file_count}")
def on_scan_finished(self, items: List[Dict], dir_count: int, file_count: int, total_size: int):
"""掃描完成處理"""
self.current_items = items
self.display_results(items, dir_count, file_count, total_size)
self.set_controls_enabled(True)
self.progress_label.setText("掃描完成")
self.statusBar().showMessage(f"掃描完成: {len(items)} 個項目")
def on_scan_error(self, error_message: str):
"""掃描錯誤處理"""
self.set_controls_enabled(True)
self.progress_label.setText("掃描錯誤")
QMessageBox.critical(self, "錯誤", error_message)
self.statusBar().showMessage("掃描失敗")
def on_directory_loaded(self, directory_path: str):
"""目錄加載完成"""
self.folder_path_input.setText(directory_path)
def display_results(self, items: List[Dict], dir_count: int, file_count: int, total_size: int):
"""顯示掃描結果"""
# 更新統(tǒng)計信息
total_count = dir_count + file_count
self.total_count_label.setText(f"總計: {total_count}")
self.total_size_label.setText(f"總大小: {self.format_size(total_size)}")
# 清空現(xiàn)有內(nèi)容
self.tree_widget.clear()
self.text_view.clear()
if not items:
self.text_view.append("文件夾為空")
return
# 排序項目
sorted_items = self.sort_items(items)
# 填充樹形視圖
self.populate_tree_view(sorted_items)
# 填充文本視圖
self.populate_text_view(sorted_items, dir_count, file_count, total_size)
def sort_items(self, items: List[Dict]) -> List[Dict]:
"""排序項目列表"""
if self.sort_method == SortMethod.NAME_ASC:
return sorted(items, key=lambda x: x['name'].lower())
elif self.sort_method == SortMethod.NAME_DESC:
return sorted(items, key=lambda x: x['name'].lower(), reverse=True)
elif self.sort_method == SortMethod.SIZE_ASC:
return sorted(items, key=lambda x: x['size'])
elif self.sort_method == SortMethod.SIZE_DESC:
return sorted(items, key=lambda x: x['size'], reverse=True)
elif self.sort_method == SortMethod.DATE_ASC:
return sorted(items, key=lambda x: x['mod_time'])
elif self.sort_method == SortMethod.DATE_DESC:
return sorted(items, key=lambda x: x['mod_time'], reverse=True)
elif self.sort_method == SortMethod.TYPE_ASC:
return sorted(items, key=lambda x: x['extension'])
else:
return items
def populate_tree_view(self, items: List[Dict]):
"""填充樹形視圖"""
for item in items:
tree_item = QTreeWidgetItem(self.tree_widget)
tree_item.setText(0, item['name'])
tree_item.setText(1, item['size_str'] if self.show_file_size else "")
tree_item.setText(2, item['mod_time'].strftime("%Y-%m-%d %H:%M:%S"))
tree_item.setText(3, item['extension'])
# 設置圖標和顏色
if item['is_dir']:
tree_item.setForeground(0, QColor(0, 0, 255)) # 藍色顯示文件夾
else:
tree_item.setForeground(0, QColor(0, 0, 0)) # 黑色顯示文件
def populate_text_view(self, items: List[Dict], dir_count: int, file_count: int, total_size: int):
"""填充文本視圖"""
self.text_view.append(f"?? 文件夾: {self.current_folder}")
self.text_view.append("=" * 60)
self.text_view.append(f"?? 統(tǒng)計信息: {dir_count} 個文件夾, {file_count} 個文件, 總大小: {self.format_size(total_size)}")
self.text_view.append("")
# 顯示文件夾
if dir_count > 0:
self.text_view.append("?? 文件夾列表:")
self.text_view.append("-" * 40)
for item in items:
if item['is_dir']:
self.text_view.append(f" ?? {item['name']}")
# 顯示文件
if file_count > 0:
self.text_view.append("")
self.text_view.append("?? 文件列表:")
self.text_view.append("-" * 40)
for item in items:
if not item['is_dir']:
size_info = f" ({item['size_str']})" if self.show_file_size else ""
self.text_view.append(f" ?? {item['name']}{size_info}")
def format_size(self, size_bytes: int) -> str:
"""格式化大小顯示"""
if size_bytes == 0:
return "0 B"
units = ["B", "KB", "MB", "GB", "TB"]
unit_index = 0
size = float(size_bytes)
while size >= 1024 and unit_index < len(units) - 1:
size /= 1024
unit_index += 1
return f"{size:.2f} {units[unit_index]}"
def refresh_content(self):
"""刷新內(nèi)容"""
if self.current_folder:
self.start_scanning()
else:
QMessageBox.information(self, "提示", "請先選擇文件夾")
def on_item_double_clicked(self, item: QTreeWidgetItem, column: int):
"""處理項目雙擊事件"""
item_name = item.text(0)
item_path = os.path.join(self.current_folder, item_name)
if os.path.isdir(item_path):
self.folder_path_input.setText(item_path)
self.load_folder(item_path)
def on_sort_changed(self, sort_text: str):
"""處理排序方式改變"""
for method in SortMethod:
if method.value == sort_text:
self.sort_method = method
break
if self.current_items:
self.display_results(
self.current_items,
int(self.folder_count_label.text().split(": ")[1]),
int(self.file_count_label.text().split(": ")[1]),
self.parse_size(self.total_size_label.text().split(": ")[1])
)
def parse_size(self, size_str: str) -> int:
"""解析大小字符串為字節(jié)數(shù)"""
if size_str == "0 B":
return 0
units = {"B": 1, "KB": 1024, "MB": 1024**2, "GB": 1024**3, "TB": 1024**4}
size, unit = size_str.split()
size = float(size)
return int(size * units[unit])
def on_show_hidden_changed(self, state: int):
"""處理顯示隱藏文件選項改變"""
self.include_hidden = state == Qt.Checked
if self.current_folder:
self.refresh_content()
def on_show_size_changed(self, state: int):
"""處理顯示文件大小選項改變"""
self.show_file_size = state == Qt.Checked
if self.current_items:
self.display_results(
self.current_items,
int(self.folder_count_label.text().split(": ")[1]),
int(self.file_count_label.text().split(": ")[1]),
self.parse_size(self.total_size_label.text().split(": ")[1])
)
def set_controls_enabled(self, enabled: bool):
"""設置控件啟用狀態(tài)"""
self.select_btn.setEnabled(enabled)
self.refresh_btn.setEnabled(enabled)
self.folder_path_input.setEnabled(enabled)
self.sort_combo.setEnabled(enabled)
self.hidden_checkbox.setEnabled(enabled)
self.size_checkbox.setEnabled(enabled)
def closeEvent(self, event):
"""處理窗口關閉事件"""
if self.scanner_thread and self.scanner_thread.isRunning():
self.scanner_thread.cancel()
self.scanner_thread.wait()
event.accept()
def main():
"""應用程序主入口"""
# 創(chuàng)建QApplication實例
app = QApplication(sys.argv)
# 設置應用程序屬性
app.setApplicationName("文件夾內(nèi)容查看器")
app.setApplicationVersion("2.0.0")
app.setOrganizationName("FileExplorer")
# 創(chuàng)建并顯示主窗口
viewer = FolderContentViewer()
viewer.show()
# 執(zhí)行應用程序主循環(huán)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
結果如下:

數(shù)學原理與性能優(yōu)化
在文件系統(tǒng)遍歷算法中,時間復雜度分析是一個重要的考量因素。對于一個包含n個項目的文件夾,我們的算法時間復雜度為O(n),空間復雜度同樣為O(n)。進度更新的數(shù)學表達為:
P(t)=(Nprocessed(t)/Ntotal)×100%
其中P(t)表示時刻t的進度百分比,Nprocessed(t)表示已處理的項目數(shù)量,Ntotal表示總項目數(shù)量。
文件大小格式化的算法基于對數(shù)計算:
sizeformatted=sizebytes/1024k??
其中k是滿足sizeformatted<1024的最小整數(shù),對應的單位從集合{B,KB,MB,GB,TB}中選擇。
功能特性深度分析
多線程架構設計
應用程序采用生產(chǎn)者−消費者模式,通過QThread實現(xiàn)后臺文件掃描,確保界面響應性。掃描線程通過信號槽機制與主線程通信,實現(xiàn)了:
- 實時進度反饋:每秒多次更新進度信息
- 取消支持:用戶可隨時中斷長時間操作
- 錯誤處理:完善的異常捕獲和用戶提示
- 內(nèi)存管理:限制最大處理項目數(shù),防止內(nèi)存溢出
用戶界面優(yōu)化
界面設計遵循Fitts定律和Hick定律,通過以下方式優(yōu)化用戶體驗:
- 分層信息展示:樹形視圖提供結構化瀏覽,文本視圖提供詳細匯總
- 智能排序:支持多種排序算法,滿足不同使用場景
- 視覺反饋:使用顏色編碼區(qū)分文件和文件夾
- 快捷鍵支持:提高高級用戶的操作效率
性能監(jiān)控與調(diào)優(yōu)
通過時間復雜度分析O(n)和空間復雜度分析O(n),確保算法效率。采用以下優(yōu)化策略:
- 批量處理:每100個項目讓出CPU,避免界面凍結
- 延遲加載:只有在需要時才計算文件大小和修改時間
- 緩存機制:記住最近訪問的文件夾和排序偏好
- 資源回收:及時釋放不再需要的系統(tǒng)資源
擴展性與架構設計
當前架構支持以下擴展方向:
- 插件系統(tǒng):可通過插件添加文件預覽、批量操作等功能
- 網(wǎng)絡支持:擴展支持FTP、SFTP等網(wǎng)絡協(xié)議
- 搜索集成:集成全文搜索和過濾功能
- 云存儲:支持對接主流云存儲服務
- 歷史記錄:實現(xiàn)瀏覽歷史管理和收藏功能
這個文件夾內(nèi)容查看器不僅實現(xiàn)了基本功能,還展示了現(xiàn)代桌面應用程序的最佳實踐,包括響應式設計、多線程處理、友好的用戶交互等特性,為用戶提供了高效、穩(wěn)定的文件瀏覽體驗。
到此這篇關于使用Python設計與實現(xiàn)一個文件夾內(nèi)容查看器的文章就介紹到這了,更多相關Python文件夾內(nèi)容查看器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
對python中的乘法dot和對應分量相乘multiply詳解
今天小編就為大家分享一篇對python中的乘法dot和對應分量相乘multiply詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-11-11
python 實現(xiàn)求解字符串集的最長公共前綴方法
今天小編就為大家分享一篇python 實現(xiàn)求解字符串集的最長公共前綴方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07
python使用BeautifulSoup分析網(wǎng)頁信息的方法
這篇文章主要介紹了python使用BeautifulSoup分析網(wǎng)頁信息的方法,涉及Python使用BeautifulSoup模塊分析網(wǎng)頁信息的技巧,非常具有實用價值,需要的朋友可以參考下2015-04-04
python實現(xiàn)猜數(shù)字游戲(無重復數(shù)字)示例分享
這篇文章主要介紹了python實現(xiàn)猜數(shù)字游戲(無重復數(shù)字)示例,需要的朋友可以參考下2014-03-03
python多項式擬合之np.polyfit 和 np.polyld詳解
這篇文章主要介紹了python多項式擬合之np.polyfit 和 np.polyld的實例代碼,python數(shù)據(jù)擬合主要可采用numpy庫,庫的安裝可直接用pip install numpy等,需要的朋友跟隨小編一起學習吧2020-02-02
python常見問題之ModuleNotFoundError: No module nam
這篇文章主要介紹了python常見問題之ModuleNotFoundError: No module named ‘rest_framework‘解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-07-07

