当前位置: 首页 > article >正文

【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` 是更好的选择。

希望这个完整的示例对你有帮助!如果有任何问题或需要进一步的解释,请随时提问。


http://www.kler.cn/a/382911.html

相关文章:

  • 映像?什么是映像
  • 【C++动态规划 01背包】2787. 将一个数字表示成幂的和的方案数|1817
  • MySQL基础-单表查询
  • mac单独打开QT帮助文档助手
  • 将一个单链表{a1,b1,a2,b2……an,bn}拆分成 {a1.a2…an}和{bn.bn-1.……b1}
  • 【毫米波雷达(八)】车载毫米波前雷达遮挡检测功能
  • 【ChatGPT】如何通过逐步提示提高ChatGPT的细节描写
  • 工业以太网PLC无线网桥,解决用户布线难题!
  • Scala IF...ELSE 语句
  • 99.9%高质量Tick数据复盘回测ea必备工具:Tick Data Suite 使用教程
  • 2024年计算机(CS)专业秋招指南
  • Spring学习笔记_26——LWT
  • Multi-head Attention机制简介和使用示例
  • WordPress站点网站名称、logo设置
  • python语言基础-3 异常处理-3.3 抛出异常
  • ElasticSearch 简单的查询。查询存在该字段的资源,更新,统计
  • 大厂面试真题-简单说说线程池接到新任务之后的操作流程
  • 传统媒体终端移动化发展新趋势:融合开源 AI 智能名片与 S2B2C 商城小程序的创新探索
  • 【大数据技术基础 | 实验八】HBase实验:新建HBase表
  • IDEA接入OpenAI API 方法教程
  • kotlin 协程方法总结
  • 【动手学电机驱动】STM32-FOC(3)STM32 三路互补 PWM 输出
  • 【MySQL系列】字符集设置
  • 搜维尔科技:Xsens和BoB助力生物力学教育
  • 是时候用开源降低AI落地门槛了
  • 洛科威岩棉板凭借多重优势,在工业管道保温领域大放异彩