You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在DRF中通过Postman传入商品名称而非PK创建订单?

解决DRF电商API中通过商品名称+数量创建订单的问题

问题分析

当前订单模型使用ManyToManyField关联商品,但无法存储购买数量,且默认只能通过商品PK创建订单。要实现通过商品名称+数量创建订单,需要调整模型结构并自定义序列化逻辑。

解决方案

1. 调整模型结构

添加中间模型OrderItem来记录订单中每个商品的购买数量,修改PlacedOrder的items字段关联到该中间模型:

from django.db import models
from django.contrib.auth.models import User

# 保持原Product模型不变
class Product(models.Model):
    product_tag = models.CharField(max_length=10)
    name = models.CharField(max_length=100)
    description = models.CharField(max_length=500, null=True, blank=True)
    category = models.ForeignKey('Category', on_delete=models.CASCADE)  # 假设Category已定义
    price = models.IntegerField()
    stock = models.IntegerField()
    image = models.ImageField(default="default.png")
    in_stock = models.BooleanField(default=True)
    date_created = models.DateField(auto_now_add=True)

    class Meta:
        ordering = ["-date_created"]

    def __str__(self):
        return self.name

# 新增订单条目模型,记录商品和数量
class OrderItem(models.Model):
    order = models.ForeignKey('PlacedOrder', on_delete=models.CASCADE, related_name='order_items')
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=1)

    class Meta:
        unique_together = ('order', 'product')  # 避免同一订单重复添加同一商品

# 修改PlacedOrder模型,使用through关联OrderItem
class PlacedOrder(models.Model):
    ordered_by = models.ForeignKey(User, on_delete=models.CASCADE)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    phone = models.CharField(max_length=15)
    address = models.CharField(max_length=100)
    zipcode = models.CharField(max_length=100)
    items = models.ManyToManyField(Product, through=OrderItem)
    total_price = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return f"{self.ordered_by}"

2. 自定义序列化器

重写序列化器,实现商品名称+数量的输入验证、库存检查及订单创建逻辑:

from rest_framework import serializers
from .models import PlacedOrder, Product, OrderItem

class PlacedOrderSerializer(serializers.ModelSerializer):
    ordered_by = serializers.ReadOnlyField(source="ordered_by.email")
    # 自定义items字段,接收[{name: 商品名, quantity: 数量}]格式的数据
    items = serializers.ListField(
        child=serializers.DictField(
            fields={'name': serializers.CharField(), 'quantity': serializers.CharField()}
        )
    )

    class Meta:
        model = PlacedOrder
        fields = (
            "id",
            "created_at",
            "ordered_by",
            "first_name",
            "last_name",
            "phone",
            "address",
            "zipcode",
            "items",
            "total_price",
        )
        # 可选:若要自动计算总价,添加该行并移除输入中的total_price
        # read_only_fields = ['total_price']

    def validate_items(self, value):
        """验证商品名称、数量及库存"""
        for item in value:
            # 检查必填字段
            if not item.get('name') or not item.get('quantity'):
                raise serializers.ValidationError("每个商品必须包含name和quantity字段")
            
            # 验证数量为正整数
            try:
                quantity = int(item['quantity'])
                if quantity <= 0:
                    raise serializers.ValidationError("商品数量必须大于0")
            except ValueError:
                raise serializers.ValidationError("quantity必须是有效整数")
            
            # 检查商品是否存在
            product = Product.objects.filter(name=item['name']).first()
            if not product:
                raise serializers.ValidationError(f"商品「{item['name']}」不存在")
            
            # 检查库存是否充足
            if product.stock < quantity:
                raise serializers.ValidationError(f"商品「{item['name']}」库存不足,当前库存:{product.stock}")
        
        return value

    def create(self, validated_data):
        """创建订单及关联的订单条目"""
        # 取出商品数据,单独处理
        items_data = validated_data.pop('items')
        # 创建订单基本信息
        order = PlacedOrder.objects.create(**validated_data)
        
        total_price = 0
        # 处理每个商品条目
        for item_data in items_data:
            product = Product.objects.get(name=item_data['name'])
            quantity = int(item_data['quantity'])
            # 创建订单条目
            OrderItem.objects.create(order=order, product=product, quantity=quantity)
            # 计算总价(若开启自动计算,取消下面注释并设置total_price为只读)
            # total_price += product.price * quantity
        
        # 自动设置总价(可选)
        # order.total_price = total_price
        # order.save()
        
        return order

3. 视图保持原有逻辑

原视图无需修改,perform_create已处理订单所属用户:

from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
from .models import PlacedOrder
from .serializers import PlacedOrderSerializer

class CreateOrder(generics.ListCreateAPIView):
    permission_classes = [IsAuthenticated]
    queryset = PlacedOrder.objects.all()
    serializer_class = PlacedOrderSerializer

    def perform_create(self, serializer):
        serializer.save(ordered_by=self.request.user)

4. 测试请求示例

Postman中使用以下格式请求:

{
    "first_name": "yes",
    "last_name": "no",
    "phone": "0100000000",
    "address": "whatever address",
    "zipcode": "254",
    "items": [
        {"name": "商品A", "quantity": 2},
        {"name": "商品B", "quantity": 1}
    ],
    "total_price": 69
}

若开启自动计算总价,请求中可移除total_price字段,序列化器会自动计算并填充。


内容的提问来源于stack exchange,提问作者Atef Arman Shishir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 14:45:36