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

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,提问作者김석범

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 19:40:11