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

DRF创建Item时自动填充user_id失败,如何正确实现?

创建Item时自动填充user_id错误的修复方案

问题描述

创建Item对象时,希望后端自动保存当前请求用户的user_id,无需客户端传递该参数。已在views.py重写perform_create方法传入当前用户,也在serializers.py配置了user_id字段,但保存的user_id却是Item的id(例如正确值应为8,实际存成18)。

代码片段

models.py

class User(AbstractUser):
    username = None
    email = models.EmailField(max_length=255, unique=True)
    
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []
    
    objects = UserManager()
    social_profile = models.URLField(null=True,blank=True)
    realname = models.CharField(max_length=50, blank=True)
    nickname = models.CharField(max_length=50, null=True, unique=True) 
    address = models.CharField(max_length=200, blank=True)
    phone = models.CharField(max_length=100, blank=True)

    def __str__(self):
        return self.email

class Item(models.Model):
    user_id = models.ForeignKey(User, related_name='item_sets', on_delete=models.CASCADE)
    category_id = models.ForeignKey(Category, related_name='item_sets', on_delete=models.DO_NOTHING)
    description = models.TextField()
    feature = models.TextField()
    product_defect = models.TextField()
    size = models.CharField(max_length=6)
    height = models.DecimalField(max_digits=4, decimal_places=1, default=0)
    weight = models.DecimalField(max_digits=4, decimal_places=1, default=0)
    condition = models.CharField(max_length=20)
    price = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    sold_out = models.BooleanField(default=False)

    def __str__(self):
        return self.description

views.py

class ItemViewSet(ModelViewSet):
    queryset = Item.objects.all()
    serializer_class = ItemSerializer
    filter_backends = [SearchFilter, OrderingFilter]
    search_fields = ['description']    # ?search=
    ordering_fields = ['created_at']  # ?ordering=
    ordering = ['-created_at']
    authentication_classes = (JWTCookieAuthentication,)

    # create
    def create(self, request, *args, **kwargs):
        city = request.data['city']
        gu = request.data['gu']
        dong = request.data['dong']
        if city is not None and gu is not None and dong is not None:
            location = Location.objects.get(
                Q(city=city) & Q(gu=gu) & Q(dong=dong)
            )
        else:
            return Response(
                {"message": "주소정보를 모두 입력해주세요."},
                status=status.HTTP_400_BAD_REQUEST
            )
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        new_item = Item.objects.get(id=serializer.data['id'])
        headers = self.get_success_headers(serializer.data)
        LocationSet.objects.create(item_id=new_item, location_id=location)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    # here!
    def perform_create(self, serializer):
        serializer.save(user_id=self.request.user)

serializers.py

class ItemSerializer(serializers.ModelSerializer):
    photos = PhotoSerializer(source='photo_sets', many=True, read_only=True)
    style_photos = StylePhotoSerializer(source='style_photo_sets', many=True, read_only=True)
    user_id = serializers.ModelField(model_field=User()._meta.get_field('id'), required=False) #here!

    class Meta:
        model = Item
        fields = '__all__'

    # Photo, StylePhoto
    def create(self, validated_data):
        images_data = self.context['request'].FILES
        item = Item.objects.create(**validated_data)
        for photo_data in images_data.getlist('photos_data'):
            Photo.objects.create(item_id=item, photo=photo_data)
        for style_photo_data in images_data.getlist('style_photos_data'):
            StylePhoto.objects.create(item_id=item, user_id=self.context['request'].user,
                                      photo=style_photo_data)
        print(User()._meta.get_field('id'))
        return item

错误原因分析

  1. 字段命名与序列化器定义冲突:模型中user_id是ForeignKey字段,而序列化器手动定义了user_id为User的id字段,导致Django无法正确识别关联关系,出现值混淆。
  2. 序列化器create方法未正确处理传入参数:虽然perform_create传入了user,但序列化器的自定义create方法没有正确利用传入的validated_data,反而可能因为字段定义问题覆盖了正确值。

修复步骤

1. 规范模型字段命名(推荐)

Django中ForeignKey字段建议命名为关联模型的小写单数形式(如user),Django会自动在数据库生成user_id列,避免字段名混淆:

# 修改models.py中的Item类
class Item(models.Model):
    user = models.ForeignKey(User, related_name='item_sets', on_delete=models.CASCADE)
    category = models.ForeignKey(Category, related_name='item_sets', on_delete=models.DO_NOTHING)
    # 其他字段保持不变...

2. 调整序列化器,移除手动定义的user_id字段

删除序列化器中手动添加的user_id字段,让ModelSerializer自动处理关联关系,并设置该字段为只读,禁止客户端传入:

class ItemSerializer(serializers.ModelSerializer):
    photos = PhotoSerializer(source='photo_sets', many=True, read_only=True)
    style_photos = StylePhotoSerializer(source='style_photo_sets', many=True, read_only=True)

    class Meta:
        model = Item
        fields = '__all__'
        # 如果模型改了字段名用'user',没改则用'user_id'
        read_only_fields = ['user']

    def create(self, validated_data):
        images_data = self.context['request'].FILES
        # 直接使用perform_create传入的validated_data创建Item
        item = Item.objects.create(**validated_data)
        for photo_data in images_data.getlist('photos_data'):
            Photo.objects.create(item_id=item, photo=photo_data)
        for style_photo_data in images_data.getlist('style_photos_data'):
            StylePhoto.objects.create(item_id=item, user_id=self.context['request'].user,
                                      photo=style_photo_data)
        return item

3. 修正perform_create方法的参数名

对应模型字段名,传入正确的参数:

# 修改views.py中的perform_create方法
def perform_create(self, serializer):
    # 模型字段名是user就传user,是user_id则传user_id
    serializer.save(user=self.request.user)

4. 验证修复效果

重新启动服务,创建Item时,后端会自动将当前请求用户的id存入user_id列,不再出现与Item id混淆的问题。

内容的提问来源于stack exchange,提问作者Jia Kwon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 13:30:57