如何在模型字段必需时允许序列化器的空值?

0 投票
1 回答
58 浏览
提问于 2025-04-14 15:54

给定以下的 payloadmodels.pyserializers.py 文件,这些都是在 Django(DRF)中使用的:

payload

{
  "created_by": 6,
  "brand": 1,
  "foo_details": [
    {
      "quantity": 123,
      "price_estimation": 456
    },
    {
      "quantity": 789,
      "price_estimation": 1011
    }
  ]
}

models.py

class Foo(models.Model):
    created_by = models.ForeignKey(CustomUser, on_delete=models.PROTECT)
    brand = models.ForeignKey(Brand, on_delete=models.SET_NULL, null=True)
    # OTHER FIELDS HERE

class FooChild(models.Model):
    foo = models.ForeignKey(Foo, on_delete=models.CASCADE, related_name="foo_details")
    quantity = models.PositiveIntegerField(default=0)
    price_estimation = models.PositiveIntegerField(default=0)
    # OTHER FIELDS HERE

serializers.py

class FooChildSerializer(serializers.ModelSerializer):
    # foo = serializers.PrimaryKeyRelatedField(read_only=True, required=False) -> LINE 2

    class Meta:
        model = FooChild
        fields = ["id", "foo", "quantity", "price_estimation", ...]

class FooSerializer(serializers.ModelSerializer):
    # foo_details = FooChildSerializer(many=True) -> LINE 9
    # foo_details = serializers.DictField(child=serializers.CharField(), many=True) -> LINE 10

    class Meta:
        model = Foo
        fields = ["id", "created_by", "brand", "foo_details", ...]

    def create(self, validated_data):
        # I want to save the payload to `Foo` and `FooChild` inside this function at the same time, below is my unsuccessful attempt

        # print(validated_data)
        # validated_data.pop("foo_details")
        # foo = Foo.objects.create(**validated_data) -> LINE 21
        # foo_child = FooChild.objects.create(foo=foo)
        
        # return foo

我现在遇到的问题是,当我尝试发送这个 payload 时,DRF 报错说 FooChild 里的 foo 字段是必填的,这个我能理解。问题是,Fooid 只有在我创建了 foo 实例之后才会存在(在第 21 行)。我尝试告诉 DRF 在创建 FooChild 时可以忽略提供 foo 的要求,但没有成功(见上面的第 2、9、10 行)。我该怎么解决这个问题呢?

其实我有个想法,就是在 FooChild 模型里设置 null=True,但 FooChild 没有 Foo 是不可能存在的,所以我觉得这样做没有意义。谢谢你的帮助。

1 个回答

0

为了在 FooChild 的序列化器中允许 foo 字段为 null,同时在模型中保持这个字段为必填,你可以按照以下步骤修改代码:

  1. FooChildSerializer 中,去掉 foo 字段的声明。
class FooChildSerializer(serializers.ModelSerializer):
    class Meta:
        model = FooChild
        fields = ["id", "quantity", "price_estimation", ...]
  1. FooSerializer 中,更新 foo_details 字段,以便在保存 Foo 实例后处理 FooChild 实例的创建。
class FooSerializer(serializers.ModelSerializer):
    foo_details = FooChildSerializer(many=True, required=False)

    class Meta:
        model = Foo
        fields = ["id", "created_by", "brand", "foo_details", ...]

    def create(self, validated_data):
        foo_details_data = validated_data.pop("foo_details", None)

        foo = Foo.objects.create(**validated_data)

        if foo_details_data:
            for foo_detail_data in foo_details_data:
                FooChild.objects.create(foo=foo, **foo_detail_data)

        return foo

通过在序列化器中将 foo_details 字段设置为 required=False,这个字段就变成了可选的。在 create 方法中,我们使用 popvalidated_data 中提取 foo_details 数据,如果没有提供,就保持为 None

在创建 foo 实例后,我们检查 foo_details_data 是否存在。如果存在,我们会遍历这些数据并创建 FooChild 实例,将它们与 foo 实例关联起来。

这样,你就可以在创建 Foo 实例时选择是否提供 foo_details。如果提供了 foo_details,它们会在 Foo 实例创建后与之关联。

撰写回答