Django Rest Framework:如何通过分类名称而非ID创建新产品
问题描述
在Django中创建了Category和Product两个模型,通过React前端用Axios发起POST请求创建产品时,只能通过分类ID提交成功,无法使用分类名称(硬编码或下拉选择都不行),Postman测试结果一致。期望提交参数格式为:
{ "name": "Coca Cola", "category": "Beverage" }
而非当前仅能生效的格式:
{ "name": "Coca Cola", "category": 1 }
现有代码
模型代码(models.py)
class Category(models.Model): name = models.CharField(max_length=50) slug = models.SlugField(unique=True, null=True, blank=True) def save(self,*args, **kwargs): self.slug = slugify(self.name) super(Category, self).save(*args, **kwargs) def __str__(self): return self.name class Product(models.Model): upc = models.CharField(max_length=30) name = models.CharField(max_length=100) description = models.TextField(max_length=255) category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True) quantity = models.IntegerField() price = models.IntegerField() def __str__(self): return self.name
视图代码(views.py)
from .models import Product from .serializers import ProductSerializers from rest_framework import generics class ProductList(generics.ListCreateAPIView): serializer_class = ProductSerializers def get_queryset(self): queryset = Product.objects.all() return queryset class ProductDetail(generics.RetrieveUpdateDestroyAPIView): serializer_class = ProductSerializers queryset = Product.objects.all()
序列化器代码(serializers.py)
from .models import Product, Category from rest_framework import serializers class CategorySerializers(serializers.ModelSerializer): class Meta: model = Category fields="__all__" class ProductSerializers(serializers.ModelSerializer): category_name = serializers.SerializerMethodField('get_category_name') def get_category_name(self, obj): if obj.category_id: return obj.category.name class Meta: model = Product fields = '__all__'
解决方案
当前的ProductSerializers只处理了返回数据时的分类名称(category_name字段),但完全没处理创建/更新产品时接收分类名称并映射到对应Category实例的逻辑。下面提供两种可行方案:
方案一:使用SlugRelatedField(推荐)
直接修改Product序列化器,把默认的category字段替换为SlugRelatedField,指定用分类的name字段来匹配:
from .models import Product, Category from rest_framework import serializers class CategorySerializers(serializers.ModelSerializer): class Meta: model = Category fields = "__all__" class ProductSerializers(serializers.ModelSerializer): category = serializers.SlugRelatedField( queryset=Category.objects.all(), slug_field='name' # 明确用分类名称作为匹配依据 ) category_name = serializers.SerializerMethodField('get_category_name') def get_category_name(self, obj): if obj.category_id: return obj.category.name class Meta: model = Product fields = '__all__'
- 改完之后,提交请求时直接传
category: "Beverage",序列化器会自动查找名称为Beverage的Category实例,关联到新创建的Product上。 - 如果提交的分类名称不存在,会返回序列化验证错误,符合接口规范。
方案二:自定义创建逻辑(支持自动创建分类)
如果需要实现「提交的分类名称不存在时自动创建分类」的功能,可以重写序列化器的create方法:
class ProductSerializers(serializers.ModelSerializer): category_name = serializers.SerializerMethodField('get_category_name') # 将category字段设为仅写入,接收分类名称 category = serializers.CharField(write_only=True) def get_category_name(self, obj): if obj.category_id: return obj.category.name def create(self, validated_data): # 从验证数据中取出分类名称 category_name = validated_data.pop('category') # 查找分类,不存在则创建(get_or_create返回实例和是否创建的布尔值) category, _ = Category.objects.get_or_create(name=category_name) # 把分类实例放回验证数据,创建产品 validated_data['category'] = category return super().create(validated_data) class Meta: model = Product fields = '__all__'
- 这种方式下,不管分类是否存在,都能成功创建关联对应分类的产品。
write_only=True确保返回产品数据时,不会把分类名称返回到category字段(如果需要返回分类名称,保留category_name字段即可)。
测试验证
修改完成后,用Postman或React前端发送如下请求:
{ "upc": "123456", "name": "Coca Cola", "description": "碳酸饮料", "category": "Beverage", "quantity": 100, "price": 3 }
即可成功创建关联指定分类的产品。
内容的提问来源于stack exchange,提问作者Ajay GC
相关产品推荐
相关产品推荐

