如何在 post 请求中正确保存嵌套对象?

How to save a nested object in a post request correctly?

我正在尝试处理 post 请求,我首先要保存一个 Tag 对象,然后它会成为 Tagging 对象的标签字段。但是,无论我尝试过什么组合,尽管 Tag Post 方法有效,当我传递一个 json 对象时,如下所示:

{
        "user_id": 1,
        "gameround_id": 2015594866,
        "resource_id": 2975,
        "tag": {
            "name": "TESTTAGGGG2222",
            "language": "en"
        },
        "score": 0,
        "origin": ""
    }

我不断收到这条消息:

{
    "name": [
        "This field is required."
    ],
    "language": [
        "This field is required."
    ]
}

但是,当我传递标签时,它起作用了。

这是post方法:

   def post(self, request, *args, **kwargs):
        if not isinstance(request.user, CustomUser):
            current_user_id = 1
        else:
            current_user_id = request.user.pk
        gameround = request.data.get('gameround', '')
        random_resource = request.data.get('resource', '')
        created = datetime.now()
        score = 0
        origin = ''
        name = request.data.get('name', '')
        language = request.data.get('language', '')

        tag_serializer = TagSerializer(data=request.data)
        tagging_serializer = TaggingSerializer(data=request.data)

        if tag_serializer.is_valid(raise_exception=True):
            tag_serializer.save(tag=request.data)
            if tagging_serializer.is_valid(raise_exception=True):
                tagging_serializer.save(tagging=request.data, tag=tag_serializer.data)
                return Response({"status": "success", "data": tagging_serializer.data},
                                status=status.HTTP_201_CREATED)
            # else:
            # return Response({"status": "success", "data": tag_serializer.data},status=status.HTTP_201_CREATED)
        else:
            return Response({"status": "error", "data": tag_serializer.errors},
                            status=status.HTTP_400_BAD_REQUEST)

如何在 post 方法中正确传递嵌套对象,以便不再出现此错误?

models.py

class Tag(models.Model):
    name = models.CharField(max_length=256)
    language = models.CharField(max_length=256)

    objects = models.Manager()

    def __str__(self):
        return self.name or ''

    @property
    def tags(self):
        tags = self.tagging.values('tag')
        return tags.values('tag_id', 'tag__name', 'tag__language')


class Tagging(models.Model):
    user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
    gameround = models.ForeignKey(Gameround, on_delete=models.CASCADE, related_name='taggings')
    resource = models.ForeignKey(Resource, on_delete=models.CASCADE, related_name='taggings')
    tag = models.ForeignKey(Tag, on_delete=models.CASCADE, related_name='tagging')
    created = models.DateTimeField(editable=False, null=True)
    score = models.PositiveIntegerField(default=0)
    # media_type = models.ForeignKey(Gamemode, on_delete=models.CASCADE)
    origin = models.URLField(max_length=256, blank=True, default='')

    objects = models.Manager()

    def __str__(self):
        return str(self.tag) or ''

serializers.py

class TagSerializer(serializers.ModelSerializer):

  class Meta:
    model = Tag
    fields = ('name', 'language')

  def create(self, validated_data):
    tag_data = validated_data.pop('tag')
    Tag.objects.create(**tag_data)
    return tag_data

  def to_representation(self, data):
    data = super().to_representation(data)
    return data

class TaggingSerializer(serializers.ModelSerializer):
  tag = TagSerializer(required=False, write_only=False)
  resource_id = serializers.PrimaryKeyRelatedField(queryset=Resource.objects.all(),
                                                   required=True,
                                                   source='resource',
                                                   write_only=False)
  gameround_id = serializers.PrimaryKeyRelatedField(queryset=Gameround.objects.all(),
                                                    required=False,
                                                    source='gameround',
                                                    write_only=False)
  user_id = serializers.PrimaryKeyRelatedField(queryset=CustomUser.objects.all(),
                                               required=False,
                                               source='user',
                                               write_only=False)

  class Meta:
    model = Tagging
    fields = ('id', 'user_id', 'gameround_id', 'resource_id', 'tag', 'created', 'score', 'origin')
    depth = 1

  def create(self, validated_data):
    """Create and return a new tagging"""

    tag_data = validated_data.pop('tag', None)
    if tag_data:
      tag = Tag.objects.get_or_create(**tag_data)[0]
      validated_data['tag'] = tag

    tagging = Tagging(
      user=validated_data.get("user"),
      gameround=validated_data.get("gameround"),
      resource=validated_data.get("resource"),
      tag=validated_data.get("tag"),
      created=datetime.now(),
      score=validated_data.get("score"),
      origin=validated_data.get("origin")
    )

    tagging.save()
    return tagging

  def to_representation(self, instance):
    rep = super().to_representation(instance)
    rep['tag'] = TagSerializer(instance.tag).data
    return rep

在您的 post 方法中,您制作了

tag_serializer = TagSerializer(data=request.data)
if tag_serializer.is_valid(raise_exception=True):
    # the rest of the code

这意味着您的数据将传递给 TagSerializer 因此 DRF 将检查此序列化程序的字段是否在请求正文中,并且未提供,因为此数据不属于此序列化程序, 它属于 TaggingSerializer 所以这会给你一个错误 This field is required

因此,您只需要将请求数据发送到 TaggingSerializer,尝试一下,让我们看看会发生什么,我建议使用 serializers.Serializer 而不是使用 serializers.ModelSerializer更好的表现