【django】Django REST Framework 构建 API:APIView 与 ViewSet
目录
1、APIView
2、ViewSet
3、APIVIew例子
3.1 模型定义
3.2 序列化器定义
3.3 使用视图
3.3.1 ProductListCreateAPIView 类
3.3.2 ProductRetrieveUpdateDestroyAPIView 类
3.4 配置url
3.5 测试
3.5.1 查询全部
3.5.2 添加产品
3.5.3 查询单个产品
3.5.4 修改单个产品
3.5.5 删除单个产品
4、VIEWSet例子
4.1 模型定义
4.2 序列化器定义
4.3 使用视图
4.4 配置url
4.5 测试
4.5.1 查询全部
4.5.2 添加产品
4.5.3 查询单个产品
4.5.4 修改单个产品
4.5.5 删除单个产品
5、总结
5.1 APIView
5.2 ViewSet
前言:针对drf框架对比APIView与ViewSet的优缺点与示例,建议先看上一篇【django】Django REST Framework 序列化与反序列化详解-CSDN博客
1、APIView
APIView 类是 DRF 中最基本的类视图。它继承自 Django 的 View 类,并添加了对请求解析、认证、权限检查等的支持。使用 APIView 可以让你更细粒度地控制每个 HTTP 方法的行为。
优点
细粒度控制:你可以完全控制每个 HTTP 方法的行为。
灵活性:适用于需要复杂逻辑的场景。
缺点
冗余代码:对于简单的 CRUD 操作,可能会导致代码重复。
维护成本:随着功能增加,代码量会变得庞大且难以维护。
2、ViewSet
ViewSet 提供了一种更简洁的方式来处理常见的 CRUD 操作。它允许你将多个相关的操作组合在一起,从而减少代码冗余并提高可维护性。
优点
简洁:自动处理常见的 CRUD 操作,减少了代码量。
一致性:提供了一致的 URL 结构和行为。
易于扩展:可以通过覆盖方法轻松扩展功能。
缺点
抽象程度高:对于非常复杂的业务逻辑,可能需要更多的定制。
学习曲线:初学者可能需要时间来理解 ViewSet 的工作原理。
3、APIVIew例子
3.1 模型定义
假设我们有一个简单的库存管理系统,包含以下三张表:Category(类别)、Product(产品)和 Supplier(供应商)。我们将基于这些表来实现类似于之前 Book 表的 CRUD 操作
# newapp/models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True, null=True)
def __str__(self):
return self.name
class Supplier(models.Model):
name = models.CharField(max_length=100)
contact_person = models.CharField(max_length=100, blank=True, null=True)
email = models.EmailField(blank=True, null=True)
phone = models.CharField(max_length=20, blank=True, null=True)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=255)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock_quantity = models.IntegerField()
description = models.TextField(blank=True, null=True)
def __str__(self):
return self.name
3.2 序列化器定义
# newapp/serializers.py
from rest_framework import serializers
from .models import Category, Product, Supplier
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name', 'description']
class SupplierSerializer(serializers.ModelSerializer):
class Meta:
model = Supplier
fields = ['id', 'name', 'contact_person', 'email', 'phone']
class ProductSerializer(serializers.ModelSerializer):
# 用于写入的字段
category_id = serializers.PrimaryKeyRelatedField(
queryset=Category.objects.all(),
source='category',
write_only=True,
required=True # 确保这个字段是必填的
)
supplier_id = serializers.PrimaryKeyRelatedField(
queryset=Supplier.objects.all(),
source='supplier',
write_only=True,
required=True # 确保这个字段是必填的
)
# 用于读取的嵌套字段
category = CategorySerializer(read_only=True)
supplier = SupplierSerializer(read_only=True)
class Meta:
model = Product
fields = [
'id', 'name', 'category_id', 'category', 'supplier_id', 'supplier',
'price', 'stock_quantity', 'description'
]
def create(self, validated_data):
category = validated_data.pop('category')
supplier = validated_data.pop('supplier')
product = Product.objects.create(
category=category,
supplier=supplier,
**validated_data
)
return product
def update(self, instance, validated_data):
if 'category' in validated_data:
instance.category = validated_data.pop('category')
if 'supplier' in validated_data:
instance.supplier = validated_data.pop('supplier')
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance
def validate(self, data):
if 'category' not in data or 'supplier' not in data:
raise serializers.ValidationError("Both category_id and supplier_id are required.")
return data
3.3 使用视图
我们定义两个基于 APIView 的视图类,分别处理产品的列表和创建操作,以及单个产品的获取、更新和删除操作。
3.3.1 ProductListCreateAPIView 类
# newapp/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Product
from .serializers import ProductSerializer
class ProductListCreateAPIView(APIView):
def get(self, request):
products = Product.objects.all()
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
def post(self, request):
serializer = ProductSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
3.3.2 ProductRetrieveUpdateDestroyAPIView
类
class ProductRetrieveUpdateDestroyAPIView(APIView):
def get_object(self, pk):
try:
return Product.objects.get(pk=pk)
except Product.DoesNotExist:
return None
def get(self, request, pk):
product = self.get_object(pk)
if not product:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = ProductSerializer(product)
return Response(serializer.data)
def put(self, request, pk):
product = self.get_object(pk)
if not product:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = ProductSerializer(product, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
product = self.get_object(pk)
if not product:
return Response(status=status.HTTP_404_NOT_FOUND)
product.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
3.4 配置url
# newapp/urls.py
from django.urls import path
from .views import ProductListCreateAPIView, ProductRetrieveUpdateDestroyAPIView
urlpatterns = [
path('products/', ProductListCreateAPIView.as_view(), name='product-list-create'),
path('products/<int:pk>/', ProductRetrieveUpdateDestroyAPIView.as_view(), name='product-retrieve-update-destroy'),
]
3.5 测试
3.5.1 查询全部
http://127.0.0.1:8000/drf/products/
[
{
"id": 1,
"name": "空调",
"category": {
"id": 1,
"name": "家电",
"description": ""
},
"supplier": {
"id": 3,
"name": "格力",
"contact_person": "董明珠",
"email": null,
"phone": null
},
"price": "3459.00",
"stock_quantity": 100,
"description": ""
},
{
"id": 2,
"name": "空调",
"category": {
"id": 1,
"name": "家电",
"description": ""
},
"supplier": {
"id": 2,
"name": "海信",
"contact_person": "贾少谦",
"email": null,
"phone": null
},
"price": "2300.00",
"stock_quantity": 5,
"description": ""
},
{
"id": 3,
"name": "电视",
"category": {
"id": 1,
"name": "家电",
"description": ""
},
"supplier": {
"id": 1,
"name": "TCL",
"contact_person": "彭攀",
"email": null,
"phone": null
},
"price": "2500.00",
"stock_quantity": 5,
"description": ""
},
{
"id": 4,
"name": "电视",
"category": {
"id": 1,
"name": "家电",
"description": ""
},
"supplier": {
"id": 2,
"name": "海信",
"contact_person": "贾少谦",
"email": null,
"phone": null
},
"price": "2700.00",
"stock_quantity": 2,
"description": ""
},
{
"id": 5,
"name": "热水器",
"category": {
"id": 2,
"name": "厨卫",
"description": ""
},
"supplier": {
"id": 4,
"name": "万家乐",
"contact_person": "罗潘",
"email": null,
"phone": null
},
"price": "1600.00",
"stock_quantity": 5,
"description": ""
}
]
3.5.2 添加产品
http://127.0.0.1:8000/drf/products/
请求体
{
"name": "电视",
"category_id": 1,
"supplier_id": 5,
"price": "2599.99",
"stock_quantity": 50,
"description": "新增库存"
}
3.5.3 查询单个产品
http://127.0.0.1:8000/drf/products/8/
3.5.4 修改单个产品
http://127.0.0.1:8000/drf/products/8/
请求体
{
"name": "电视333",
"category_id": 1,
"supplier_id": 5,
"price": "2599.99",
"stock_quantity": 1,
"description": "修改库存"
}
3.5.5 删除单个产品
http://127.0.0.1:8000/drf/products/8/
4、VIEWSet例子
4.1 模型定义
模型不变,同上
4.2 序列化器定义
不变,同上
4.3 使用视图
# newapp/views.py
from rest_framework import viewsets
from .models import Category, Product, Supplier
from .serializers import CategorySerializer, ProductSerializer, SupplierSerializer
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
class SupplierViewSet(viewsets.ModelViewSet):
queryset = Supplier.objects.all()
serializer_class = SupplierSerializer
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
4.4 配置url
# newapp/urls.py
from django.urls import path
from rest_framework.routers import SimpleRouter
from .views import CategoryViewSet, ProductViewSet, SupplierViewSet
# 创建一个默认的路由器实例
router = DefaultRouter()
# 注册视图集到路由器
router.register(r'categories', CategoryViewSet)
router.register(r'suppliers', SupplierViewSet)
router.register(r'product', ProductViewSet)
urlpatterns += router.urls
4.5 测试
4.5.1 查询全部
http://127.0.0.1:8000/drf/product/
[
{
"id": 1,
"name": "空调",
"category": {
"id": 1,
"name": "家电",
"description": ""
},
"supplier": {
"id": 3,
"name": "格力",
"contact_person": "董明珠",
"email": null,
"phone": null
},
"price": "3459.00",
"stock_quantity": 100,
"description": ""
},
{
"id": 2,
"name": "空调",
"category": {
"id": 1,
"name": "家电",
"description": ""
},
"supplier": {
"id": 2,
"name": "海信",
"contact_person": "贾少谦",
"email": null,
"phone": null
},
"price": "2300.00",
"stock_quantity": 5,
"description": ""
},
{
"id": 3,
"name": "电视",
"category": {
"id": 1,
"name": "家电",
"description": ""
},
"supplier": {
"id": 1,
"name": "TCL",
"contact_person": "彭攀",
"email": null,
"phone": null
},
"price": "2500.00",
"stock_quantity": 5,
"description": ""
},
{
"id": 4,
"name": "电视",
"category": {
"id": 1,
"name": "家电",
"description": ""
},
"supplier": {
"id": 2,
"name": "海信",
"contact_person": "贾少谦",
"email": null,
"phone": null
},
"price": "2700.00",
"stock_quantity": 2,
"description": ""
},
{
"id": 5,
"name": "热水器",
"category": {
"id": 2,
"name": "厨卫",
"description": ""
},
"supplier": {
"id": 4,
"name": "万家乐",
"contact_person": "罗潘",
"email": null,
"phone": null
},
"price": "1600.00",
"stock_quantity": 5,
"description": ""
},
{
"id": 6,
"name": "电视",
"category": {
"id": 1,
"name": "家电",
"description": ""
},
"supplier": {
"id": 5,
"name": "长虹",
"contact_person": "柳江",
"email": null,
"phone": null
},
"price": "2599.99",
"stock_quantity": 50,
"description": "新增库存"
}
]
4.5.2 添加产品
http://127.0.0.1:8000/drf/product/
请求体
{
"name": "电视888待删除",
"category_id": 1,
"supplier_id": 5,
"price": "1222",
"stock_quantity": 1,
"description": ""
}
4.5.3 查询单个产品
http://127.0.0.1:8000/drf/product/1/
4.5.4 修改单个产品
http://127.0.0.1:8000/drf/product/9/
4.5.5 删除单个产品
http://127.0.0.1:8000/drf/product/9/
5、总结
通过上述两个示例,你可以看到使用 `APIView` 和 `ViewSet` 实现 CRUD 操作的不同方式。以下是两者的比较:
5.1 APIView
- - 更细粒度的控制。
- - 适用于需要复杂逻辑的场景。
- - 代码量相对较多,但更灵活。
5.2 ViewSet
- - 自动处理常见的 CRUD 操作。
- - 代码简洁,易于维护。
- - 提供了一致的 URL 结构和行为。
选择哪种方式取决于你的具体需求。如果你需要更多的灵活性和细粒度的控制,可以选择 `APIView`。如果你希望快速实现标准的 CRUD 功能并减少代码量,`ViewSet` 是更好的选择。
希望这个完整的示例对你有帮助!如果有任何问题或需要进一步的解释,请随时提问。