DjangoRestFramework ModelViewSet声明user_id报错:comparelist需user_id
问题:DRF创建CompareList时提示"comparelist required user_id"错误
外键层级为User>CompareList>Obj,使用DjangoRestFramework的ModelViewSet开发接口,发送POST请求创建CompareList时收到错误提示:"comparelist required user_id",相关代码如下:
accounts/models.py
from django.db import models from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin from .managers import UserManager from core.models import TimestampedModel class User(AbstractBaseUser, PermissionsMixin, TimestampedModel): email = models.EmailField(max_length=30, unique=True, null=False, blank=False) is_superuser = models.BooleanField(default=False) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) objects = UserManager() USERNAME_FIELD = 'email'
accounts/serializers.py
from .models import User from rest_framework import serializers class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = '__all__' def create(self, validated_data): user = User.objects.create_user( email = validated_data['email'], password = validated_data['password'] ) return user
ImageConverters/models.py
from django.db import models from accounts.models import User class CompareList(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="owner") id = models.AutoField(primary_key=True) created_at = models.DateTimeField(auto_now_add=True) class Obj(models.Model): comparelist = models.ForeignKey(CompareList, on_delete=models.CASCADE) object_name = models.CharField(max_length=100) image = models.URLField(max_length=500)
ImageConverters/serializers.py
from .models import CompareList, Obj from rest_framework import serializers class ObjSerializer(serializers.ModelSerializer): class Meta: model = Obj fields = ['object_name', 'image'] class CompareListSerializer(serializers.ModelSerializer): objs = ObjSerializer(many=True, read_only=True) class Meta: model = CompareList fields = ['id', 'objs'] def create(self, validated_data): # print(dir(self)) print(self) print('------------------') images_data = self.context['request'].FILES texts_data = self.context['request'].data comparelist = CompareList.objects.create(**validated_data) print(texts_data) for image_data, text_data in zip(images_data.getlist('image'), texts_data.getlist('object_name')): print(image_data, text_data) Obj.objects.create(comparelist=comparelist, image=image_data, object_name=text_data) return comparelist
ImageConverters/views.py
from rest_framework.viewsets import ModelViewSet from .models import CompareList, Obj from .serializers import CompareListSerializer, ObjSerializer class CompareListCreate(ModelViewSet): queryset = CompareList.objects.all() serializer_class = CompareListSerializer class ObjListCreate(ModelViewSet): queryset = Obj.objects.all() serializer_class = ObjSerializer
错误原因
CompareList模型的user外键是必填字段,但当前的CompareListSerializer既没有在序列化字段中包含user/user_id,也没有在创建逻辑中自动关联当前登录用户,导致创建CompareList时缺少user字段的值,触发数据库的非空约束错误。
另外,原代码中Obj模型的image是URLField(存储字符串URL),但序列化器却从request.FILES(上传文件)中取值,这也是逻辑矛盾点,需要同步调整。
解决方案
1. 配置DRF身份验证
确保只有登录用户才能访问接口,在项目settings.py中添加:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', # 若使用Token验证 ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ] }
2. 修改CompareListSerializer
修改序列化器,自动将当前登录用户关联到CompareList,同时修正Obj的创建逻辑:
class CompareListSerializer(serializers.ModelSerializer): objs = ObjSerializer(many=True, read_only=True) class Meta: model = CompareList fields = ['id', 'objs', 'user'] read_only_fields = ['user'] # 标记为只读,由后端自动赋值 def create(self, validated_data): # 从请求上下文获取当前登录用户 current_user = self.context['request'].user # 创建CompareList时传入user字段 comparelist = CompareList.objects.create(user=current_user, **validated_data) # 处理Obj数据:从request.data中取URL,而非FILES request_data = self.context['request'].data object_names = request_data.getlist('object_name') image_urls = request_data.getlist('image') for name, url in zip(object_names, image_urls): Obj.objects.create( comparelist=comparelist, object_name=name, image=url ) return comparelist
若实际需求是上传图片文件而非传入URL,需将
Obj模型的image字段改为:image = models.ImageField(upload_to='compare_images/')同时在
settings.py中配置媒体文件路径,并确保前端以multipart/form-data格式上传文件。
3. 视图保持原有配置即可
原ModelViewSet配置无需修改,只要身份验证和权限配置正确,就能正常获取当前用户。
内容的提问来源于stack exchange,提问作者김석범
相关产品推荐
相关产品推荐

