DRF講解
1. 什么是 Django 和 Django REST Framework?
在深入 ModelViewSet
之前,我們先簡單了解一下背景知識:
- Django 是一個基于 Python 的 Web 開發框架,旨在幫助開發者快速構建安全、可擴展的 Web 應用。它遵循“不要重復自己”(DRY)和“快速開發”的原則,提供了許多內置功能,比如 ORM(對象關系映射)、用戶認證、模板引擎等。
- Django REST Framework (DRF) 是 Django 的擴展庫,專門用于構建 RESTful API。它讓開發者可以輕松地將 Django 模型轉換為 API 端點,供前端或其他服務調用。
- ViewSet 是 DRF 提供的一種高級視圖類,
ModelViewSet
是其中最常用的子類,用于處理模型的 CRUD(創建、讀取、更新、刪除)操作。
2. 什么是 ModelViewSet
?
ModelViewSet
是 Django REST Framework 中的一個類,位于 rest_framework.viewsets
模塊。它是一個高度抽象的工具,集成了處理模型數據的所有常見操作(CRUD),讓你只需編寫很少的代碼,就能實現完整的 API 功能。
核心特點:
- 自動生成 CRUD 端點:
ModelViewSet
自動為你的模型提供以下 HTTP 方法對應的操作:GET
:列表(list
)和詳情(retrieve
)POST
:創建(create
)PUT
/PATCH
:更新(update
/partial_update
)DELETE
:刪除(destroy
)
- 代碼簡潔:通過繼承
ModelViewSet
,你只需指定模型和序列化器(Serializer),就能自動生成這些功能。 - 靈活性:可以自定義行為,比如添加權限、過濾、排序等。
3. 使用 ModelViewSet
的步驟
創建一個簡單的“任務管理”應用,包含任務(Task)的增刪改查功能。
3.1 準備工作
環境要求
- 已安裝 Python 3(推薦 3.8 或更高版本)
- 已安裝 Django 和 Django REST Framework:
pip install django djangorestframework
創建 Django 項目
- 創建一個新的 Django 項目:
django-admin startproject task_manager cd task_manager
- 創建一個 Django 應用:
python manage.py startapp tasks
- 將
tasks
應用和rest_framework
添加到task_manager/settings.py
的INSTALLED_APPS
中:INSTALLED_APPS = [...'rest_framework','tasks', ]
3.2 定義模型
在 tasks/models.py
中定義一個簡單的 Task
模型:
from django.db import modelsclass Task(models.Model):title = models.CharField(max_length=200)description = models.TextField(blank=True)completed = models.BooleanField(default=False)created_at = models.DateTimeField(auto_now_add=True)def __str__(self):return self.title
- 解釋:
title
:任務的標題,最大長度 200 個字符。description
:任務描述,允許為空。completed
:是否完成,默認為False
。created_at
:創建時間,自動設置為當前時間。__str__
:返回任務的字符串表示,便于調試。
運行遷移命令以創建數據庫表:
python manage.py makemigrations
python manage.py migrate
3.3 創建序列化器(Serializer)
序列化器負責將模型數據轉換為 JSON 格式(或從 JSON 轉換為模型)。在 tasks/serializers.py
中創建:
from rest_framework import serializers
from .models import Taskclass TaskSerializer(serializers.ModelSerializer):class Meta:model = Taskfields = ['id', 'title', 'description', 'completed', 'created_at']
- 解釋:
ModelSerializer
是 DRF 提供的類,自動根據模型生成序列化邏輯。Meta
類指定關聯的模型(Task
)和需要序列化的字段。fields
列出要包含的字段,id
是 Django 模型自動生成的。
3.4 創建 ModelViewSet
在 tasks/views.py
中定義 ModelViewSet
:
from rest_framework import viewsets
from .models import Task
from .serializers import TaskSerializerclass TaskViewSet(viewsets.ModelViewSet):queryset = Task.objects.all()serializer_class = TaskSerializer
- 解釋:
queryset
:指定視圖使用的查詢集,這里是所有Task
對象。serializer_class
:指定序列化器,用于處理輸入輸出數據。ModelViewSet
自動提供了list
、retrieve
、create
、update
、partial_update
和destroy
方法。
3.5 配置路由
為了讓 API 可訪問,需要將 TaskViewSet
注冊到路由中。在 task_manager/urls.py
中:
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from tasks.views import TaskViewSet# 創建路由器并注冊 ViewSet
router = DefaultRouter()
router.register(r'tasks', TaskViewSet)urlpatterns = [path('admin/', admin.site.urls),path('api/', include(router.urls)),
]
- 解釋:
DefaultRouter
是 DRF 提供的工具,自動為ModelViewSet
生成 URL 路由。router.register
注冊TaskViewSet
,并將 URL 前綴設為tasks
。include(router.urls)
將路由納入主 URL 配置,API 端點將以/api/tasks/
開頭。
3.6 測試 API
- 啟動 Django 開發服務器:
python manage.py runserver
- 訪問
http://127.0.0.1:8000/api/tasks/
,你會看到 DRF 提供的交互式 API 界面。 - 測試以下功能:
- GET
/api/tasks/
:列出所有任務。 - POST
/api/tasks/
:創建新任務(通過 JSON 提交{"title": "新任務", "description": "描述", "completed": false}
)。 - GET
/api/tasks/1/
:獲取 ID 為 1 的任務詳情。 - PUT
/api/tasks/1/
:更新 ID 為 1 的任務。 - DELETE
/api/tasks/1/
:刪除 ID 為 1 的任務。
- GET
4. 深入理解 ModelViewSet
4.1 ModelViewSet
的默認行為
ModelViewSet
繼承了多個 Mixin 類,提供了以下方法:
方法 | HTTP 方法 | 功能 | URL 示例 |
---|---|---|---|
list | GET | 返回所有對象的列表 | /api/tasks/ |
retrieve | GET | 返回單個對象的詳情 | /api/tasks/1/ |
create | POST | 創建新對象 | /api/tasks/ |
update | PUT | 更新現有對象(整體更新) | /api/tasks/1/ |
partial_update | PATCH | 部分更新對象 | /api/tasks/1/ |
destroy | DELETE | 刪除對象 | /api/tasks/1/ |
這些方法由 ModelViewSet
自動實現,你無需手動編寫。
4.2 自定義 ModelViewSet
雖然 ModelViewSet
提供了默認實現,但你可以通過重寫方法或添加屬性來自定義行為。以下是一些常見自定義場景:
示例 1:添加權限控制
限制只有登錄用戶可以訪問 API。在 tasks/views.py
中:
from rest_framework import viewsets, permissions
from .models import Task
from .serializers import TaskSerializerclass TaskViewSet(viewsets.ModelViewSet):queryset = Task.objects.all()serializer_class = TaskSerializerpermission_classes = [permissions.IsAuthenticated] # 要求登錄
- 解釋:
permission_classes
指定只有通過認證的用戶才能訪問 API。 - 需要在
settings.py
中啟用認證:REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication','rest_framework.authentication.BasicAuthentication',], }
示例 2:過濾查詢集
只顯示當前用戶的任務:
class TaskViewSet(viewsets.ModelViewSet):serializer_class = TaskSerializerpermission_classes = [permissions.IsAuthenticated]def get_queryset(self):return Task.objects.filter(owner=self.request.user)
- 解釋:重寫
get_queryset
方法,根據當前用戶過濾任務。 - 需要在
Task
模型中添加owner
字段:from django.contrib.auth.models import User class Task(models.Model):owner = models.ForeignKey(User, on_delete=models.CASCADE)...
示例 3:自定義序列化器行為
假設你想在 list
操作時只返回部分字段:
class TaskViewSet(viewsets.ModelViewSet):queryset = Task.objects.all()def get_serializer_class(self):if self.action == 'list':return TaskListSerializer # 自定義序列化器return TaskSerializerclass TaskListSerializer(serializers.ModelSerializer):class Meta:model = Taskfields = ['id', 'title'] # 僅返回 id 和 title
- 解釋:
get_serializer_class
允許根據請求類型(action
)動態選擇序列化器。
6. 常見問題解答
Q1:為什么使用 ModelViewSet
而不是普通視圖?
A:ModelViewSet
提供了開箱即用的 CRUD 功能,減少重復代碼。對于快速開發 API 或原型,它非常高效。如果需要更多自定義,可以使用普通的 APIView
或 GenericAPIView
。
Q2:如何處理復雜的 API 邏輯?
A:可以重寫 ModelViewSet
的方法(如 create
、update
)或使用自定義動作(@action
裝飾器)。
Q3:如何學習更多高級功能?
A:學習 DRF 的權限、過濾、分頁和認證系統。嘗試實現 token 認證(使用 rest_framework.authtoken
或 JWT)。
面試題
簡單題目 (2 道)
1. 什么是 ModelViewSet
,它在 Django REST Framework 中有什么作用?
問題描述:請簡要說明 ModelViewSet
的定義以及它在構建 API 時的主要功能。
答案提示:
ModelViewSet
是 Django REST Framework 提供的一個高級視圖類,繼承自GenericViewSet
和多個 Mixin(如CreateModelMixin
、ListModelMixin
等)。- 它自動為模型提供 CRUD 操作(創建、讀取、更新、刪除)的 API 端點。
- 通過指定
queryset
和serializer_class
,可以快速生成 RESTful API。 - 示例:
class TaskViewSet(viewsets.ModelViewSet): queryset = Task.objects.all(); serializer_class = TaskSerializer
考察點:理解 ModelViewSet
的基本概念和用途。
2. 如何為 ModelViewSet
配置路由?
問題描述:說明如何使用 Django REST Framework 的路由器(Router)為 ModelViewSet
配置 URL 路由,并提供代碼示例。
答案提示:
- 使用
rest_framework.routers.DefaultRouter
創建路由器。 - 調用
router.register
方法注冊ModelViewSet
,指定 URL 前綴和視圖集。 - 示例:
from rest_framework.routers import DefaultRouter from .views import TaskViewSetrouter = DefaultRouter() router.register(r'tasks', TaskViewSet) urlpatterns = [path('api/', include(router.urls)), ]
- 自動生成
/api/tasks/
(列表和創建)和/api/tasks/<id>/
(詳情、更新、刪除)等端點。
考察點:掌握 ModelViewSet
與路由器的集成。
中等題目 (5 道)
3. 如何在 ModelViewSet
中限制查詢集只返回當前用戶的數據?
問題描述:假設有一個 Task
模型,包含 owner
字段(關聯到 User
模型)。如何修改 TaskViewSet
,使 GET /api/tasks/
只返回當前登錄用戶的任務?
答案提示:
- 重寫
get_queryset
方法,根據self.request.user
過濾查詢集。 - 示例:
class TaskViewSet(viewsets.ModelViewSet):serializer_class = TaskSerializerpermission_classes = [permissions.IsAuthenticated]def get_queryset(self):return Task.objects.filter(owner=self.request.user)
- 需要確保用戶已登錄(
IsAuthenticated
權限)。
考察點:理解 get_queryset
的動態過濾和權限控制。
4. 如何在 ModelViewSet
中為不同操作使用不同的序列化器?
問題描述:在 TaskViewSet
中,list
操作只需要返回 id
和 title
,而其他操作需要返回所有字段。如何實現?
答案提示:
- 重寫
get_serializer_class
方法,根據self.action
返回不同的序列化器。 - 示例:
class TaskListSerializer(serializers.ModelSerializer):class Meta:model = Taskfields = ['id', 'title']class TaskSerializer(serializers.ModelSerializer):class Meta:model = Taskfields = '__all__'class TaskViewSet(viewsets.ModelViewSet):queryset = Task.objects.all()def get_serializer_class(self):if self.action == 'list':return TaskListSerializerreturn TaskSerializer
考察點:掌握 get_serializer_class
的動態選擇機制。
5. 如何在 ModelViewSet
中添加自定義動作?
問題描述:為 TaskViewSet
添加一個自定義動作 mark_completed
,通過 POST /api/tasks/<id>/mark_completed/
將任務標記為已完成。提供代碼示例。
答案提示:
- 使用
@action
裝飾器定義自定義動作。 - 示例:
from rest_framework.decorators import action from rest_framework.response import Responseclass TaskViewSet(viewsets.ModelViewSet):queryset = Task.objects.all()serializer_class = TaskSerializer@action(detail=True, methods=['post'])def mark_completed(self, request, pk=None):task = self.get_object()task.completed = Truetask.save()serializer = self.get_serializer(task)return Response(serializer.data)
detail=True
表示該動作針對單個對象,URL 為/api/tasks/<id>/mark_completed/
。
考察點:理解 @action
裝飾器和自定義端點的實現。
6. 如何在 ModelViewSet
中實現分頁?
問題描述:當任務列表很長時,如何為 TaskViewSet
的 list
操作啟用分頁,每頁顯示 10 條記錄?
答案提示:
- 在
settings.py
中配置全局分頁,或在ViewSet
中指定分頁類。 - 示例(全局配置):
# settings.py REST_FRAMEWORK = {'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination','PAGE_SIZE': 10 }
- 或者在
ViewSet
中:class TaskViewSet(viewsets.ModelViewSet):queryset = Task.objects.all()serializer_class = TaskSerializerpagination_class = PageNumberPaginationpage_size = 10
- 訪問
/api/tasks/?page=2
獲取第二頁數據。
考察點:掌握 DRF 的分頁機制和配置。
7. 如何為 ModelViewSet
添加過濾功能?
問題描述:如何讓 TaskViewSet
支持通過查詢參數過濾任務,例如 GET /api/tasks/?completed=true
只返回已完成的任務?
答案提示:
- 使用 DRF 的
django-filter
擴展或自定義過濾。 - 安裝
django-filter
:pip install django-filter
- 示例:
# settings.py INSTALLED_APPS = [... , 'django_filters'] REST_FRAMEWORK = {'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'] }# views.py class TaskViewSet(viewsets.ModelViewSet):queryset = Task.objects.all()serializer_class = TaskSerializerfilterset_fields = ['completed']
- 訪問
/api/tasks/?completed=true
即可過濾。
考察點:理解 DRF 的過濾后端和 django-filter
的使用。
難題 (3 道)
8. 如何在 ModelViewSet
中實現復雜的權限控制?
問題描述:假設 Task
模型有一個 is_private
字段。只有任務的擁有者或超級用戶可以查看/編輯私有任務(is_private=True
),其他用戶只能訪問非私有任務。如何實現?
答案提示:
- 創建自定義權限類,繼承
permissions.BasePermission
。 - 示例:
from rest_framework import permissionsclass TaskPermission(permissions.BasePermission):def has_object_permission(self, request, view, obj):if not obj.is_private:return True # 非私有任務對所有人可訪問return obj.owner == request.user or request.user.is_superuserclass TaskViewSet(viewsets.ModelViewSet):queryset = Task.objects.all()serializer_class = TaskSerializerpermission_classes = [permissions.IsAuthenticated, TaskPermission]def get_queryset(self):if self.request.user.is_superuser:return Task.objects.all()return Task.objects.filter(models.Q(is_private=False) | models.Q(owner=self.request.user))
has_object_permission
控制單個對象的訪問權限,get_queryset
過濾列表。
考察點:高級權限控制、自定義權限類和查詢集過濾的結合。
9. 如何在 ModelViewSet
中處理嵌套序列化器?
問題描述:假設 Task
模型與 Category
模型通過外鍵關聯(task.category
)。如何在 TaskViewSet
中返回任務時包含分類的詳細信息,而不是僅返回分類 ID?
答案提示:
- 在序列化器中使用嵌套序列化器。
- 示例:
# models.py class Category(models.Model):name = models.CharField(max_length=100)class Task(models.Model):title = models.CharField(max_length=200)category = models.ForeignKey(Category, on_delete=models.CASCADE)# serializers.py class CategorySerializer(serializers.ModelSerializer):class Meta:model = Categoryfields = ['id', 'name']class TaskSerializer(serializers.ModelSerializer):category = CategorySerializer(read_only=True)class Meta:model = Taskfields = ['id', 'title', 'category']# views.py class TaskViewSet(viewsets.ModelViewSet):queryset = Task.objects.all()serializer_class = TaskSerializer
- 為支持創建/更新任務時接收分類 ID,需重寫序列化器的
create
/update
方法。
考察點:嵌套序列化器、只讀字段和復雜數據結構的處理。
10. 如何優化 ModelViewSet
的性能?
問題描述:假設 TaskViewSet
的 list
操作需要處理大量數據(例如 10 萬條任務記錄),如何優化性能以減少響應時間?
答案提示:
- 分頁:啟用分頁(參考題目 6),限制每次返回的記錄數。
- 選擇性字段加載:使用
select_related
或prefetch_related
優化數據庫查詢。class TaskViewSet(viewsets.ModelViewSet):queryset = Task.objects.select_related('category').all()serializer_class = TaskSerializer
- 緩存:使用 Django 的緩存框架或 DRF 的緩存裝飾器。
from rest_framework.decorators import cache_pageclass TaskViewSet(viewsets.ModelViewSet):queryset = Task.objects.all()serializer_class = TaskSerializer@cache_page(60 * 15) # 緩存 15 分鐘def list(self, request, *args, **kwargs):return super().list(request, *args, **kwargs)
- 索引:在數據庫中為常用查詢字段(如
owner
、completed
)添加索引。 - 異步任務:對于耗時操作(如批量更新),使用 Celery 異步處理。
考察點:性能優化技巧,包括數據庫查詢優化、緩存和異步任務。