目录
3.3.1 ProductListCreateAPIView 类
3.3.2 ProductRetrieveUpdateDestroyAPIView 类
前言:针对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` 是更好的选择。
希望这个完整的示例对你有帮助!如果有任何问题或需要进一步的解释,请随时提问。