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
错误原因分析
- 字段命名与序列化器定义冲突:模型中
user_id是ForeignKey字段,而序列化器手动定义了user_id为User的id字段,导致Django无法正确识别关联关系,出现值混淆。 - 序列化器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
相关产品推荐
相关产品推荐

