在 Django Rest Framework 中将字段分解为自己的序列化程序

Break up field into its own serializer in Django Rest Framework

假设我有简单的 Product Django 模型:

class Product:
    name = models.CharField(max_length=255, unique=True)
    created_on = models.DateField()

我正在使用 Django Rest Framework 序列化此模型。我想将 created_on 分解成它自己的对象(包括 GET 请求的响应和 POST 请求中的有效负载):

{
    "name": "MyProduct",
    "created_on": {
        "year": 2020,
        "month": 1,
        "day": 24
    }
}

这是我目前的情况:

class DateSerializer(serializers.Serializer):
    year = serializers.IntegerField()
    month = serializers.IntegerField()
    day = serializers.IntegerField()

    def validate(data):
        return datetime.date(data["year"], data["month"], data["day"])

class ProductSerializer(serialzers.ModelSerializer):
    created_on = DateSerializer()

    class Meta:
        model = Friend
        fields = ("name", "created_on")

    def create(self, validated_data):
        return Product.objects.create(**validated_data)

class ProductViewset(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

这种方法适用于 GET 请求(也就是说,我得到上面的 json)。但是,它不适用于 POST 请求(有效负载是上面的 json)。响应是 400 状态代码和消息 {'created_on': [ErrorDetail(string='This field is required.', code='required')]}.

如果我将 required=False 传递给 DateSerializer,我会看到 create 方法中的 self.initial_data<QueryDict: {'name': ['MyProduct'], 'created_on': ['year', 'month', 'day']}>。所以这些值由于某种原因消失了。

知道我在这里做错了什么以及如何让它工作吗?

class DateSerializer(serializers.Serializer):
    year = serializers.IntegerField()
    month = serializers.IntegerField()
    day = serializers.IntegerField()

    def to_internal_value(data):
        return datetime.date(data["year"], data["month"], data["day"])

覆盖validate(...)方法 ProductSerializer class as,

<b>from datetime import date</b>


class DateSerializer(serializers.Serializer):
    year = serializers.IntegerField()
    month = serializers.IntegerField()
    day = serializers.IntegerField()


class ProductSerializer(serializers.ModelSerializer):
    created_on = DateSerializer()

    <b>def validate(self, attrs):
        super().validate(attrs)
        attrs['created_on'] = date(**attrs['created_on'])
        return attrs</b>

    class Meta:
        model = Product
        fields = ("name", "created_on")

示例:

In [8]: payload = {"name": "MyProduct", "created_on": {"year": 2020, "month": 1, "day": 24}}                                                                                                                       

In [9]: serializer = ProductSerializer(data=payload)                                                                                                                                                               

In [10]: serializer.is_valid(True)                                                                                                                                                                                 
Out[10]: True

In [11]: serializer.validated_data                                                                                                                                                                                 
Out[11]: 
OrderedDict([('name', 'MyProduct'),
             ('created_on', datetime.date(2020, 1, 24))])

In [12]: product_instance = serializer.save()                                                                                                                                                                      

In [13]: product_instance.__dict__                                                                                                                                                                                 
Out[13]: 
{'_state': <django.db.models.base.ModelState at 0x7f75c0629978>,
 'id': 2,
 'name': 'MyProduct',
 'created_on': datetime.date(2020, 1, 24)}

In [14]: serializer.data                                                                                                                                                                                           
Out[14]: {'name': 'MyProduct', 'created_on': OrderedDict([('year', 2020), ('month', 1), ('day', 24)])}

想通了,我需要在请求中设置 content_type=application/json header。否则它默认为 content_type=multipart/form-data 将有效载荷展平为 {'name': ['MyProduct'], 'created_on': ['year', 'month', 'day']}.

为什么搞得这么复杂。你可以做到

class Product:
    name = models.CharField(max_length=255, unique=True)
    created_on = models.DateField()

然后是序列化器

class ProductSerializer(serialzers.ModelSerializer):

    def to_representation(self, instance):
       try:
         data = super().to_representation(instance)
         created_on = {}
         ### You should check on the syntax for this to get the right value
         created_on['year'] = data['created_on'].year 
         created_on['month'] = data['created_on'].month
         created_on['day'] = data['created_on'].day 
         data['created_on'] = created_on
       except :
          pass
       return data

    class Meta:
         model = Friend
         fields = ("name", "created_on")

或者这样:

class ProductSerializer(serialzers.ModelSerializer):
    created_on_2 = serializers.SerializerMethodField()
    
    def get_created_on_2(self, obj):
         created_on = {}
         ### You should check on the syntax for this to get the right value
         created_on['year'] = data['created_on'].year 
         created_on['month'] = data['created_on'].month
         created_on['day'] = data['created_on'].day 
         return created_on

    class Meta:
         model = Friend
         fields = ("name", "created_on", "created_on_2)

这样您仍然可以使用“created_on”的常规日期字符串值发出 POST 请求,并获得您想要的输出 有关详细信息,请查看此 to_respresentation() SerializerMethodField