【问题标题】:Django Rest Framework: AttributeError when Serializer many=False, but not when many=TrueDjango Rest Framework:当序列化器many = False时出现AttributeError,但当many = True时不会出现
【发布时间】:2015-08-20 15:02:49
【问题描述】:

我有SlideChart 模型,幻灯片只能包含一张图表。序列化数据时失败并出现以下错误:

AttributeError:在序列化程序 ChartSerializer 上尝试获取字段 csv 的值时出现 AttributeError。 序列化程序字段可能命名不正确,并且与 RelatedManager 实例上的任何属性或键都不匹配。 原始异常文本为:“RelatedManager”对象没有属性“csv”。

但是,如果我只是改变:

chart = ChartSerializer(many=True)

在我的SlideSerializer(下面的代码)中它可以工作。这使得 'chart' 属性成为 JSON 中的列表,尽管它应该是一个对象,因为列表中只能有一个图表,而不是很多(或在本例中为一个)图表对象。

这是我的模型(仅摘录相关信息):

# models.py

...

class Slide(models.Model):
    TYPE_MEDIA = 'media'
    TYPE_CHART = 'chart'
    TYPE_CHOICES = (
        (TYPE_MEDIA, 'Media'),
        (TYPE_CHART, 'Chart'),
    )

    title = models.CharField(max_length=255,)

    type = models.CharField(choices=TYPE_CHOICES, max_length=5, default=TYPE_MEDIA)

    media = models.FileField(
        verbose_name='media',
        upload_to='slides',
        null=True,
        blank=True,
    )

    internal_only = models.BooleanField(default=False)

    creation_date = models.DateTimeField(auto_now_add=True)
    modified_date = models.DateTimeField(auto_now=True)


class Chart(models.Model):
    slide = models.ForeignKey(Slide, related_name='chart')

    csv = models.FileField(
        verbose_name='csv',
        upload_to='charts'
    )

    vertical_label = models.CharField(max_length=255,)
    horizontal_label = models.CharField(max_length=255,)

    creation_date = models.DateTimeField(auto_now_add=True)
    modified_date = models.DateTimeField(auto_now=True)


class ChartSeriesLabel(models.Model):
    """Series label associated with a chart"""

    chart = models.ForeignKey(Chart, related_name='serieslabels')

    number = models.CharField(max_length=255,)
    label = models.CharField(max_length=255,)

    creation_date = models.DateTimeField(auto_now_add=True)
    modified_date = models.DateTimeField(auto_now=True)


class ChartDataLabel(models.Model):
    """Data label associated with a chart"""

    chart = models.ForeignKey(Chart, related_name='datalabels')

    date = models.DateField()
    label = models.CharField(max_length=255,)

    creation_date = models.DateTimeField(auto_now_add=True)
    modified_date = models.DateTimeField(auto_now=True)

还有我的序列化器:

# serializers.py

...

class SeriesLabelSerializer(serializers.ModelSerializer):
    class Meta:
        model = ChartSeriesLabel
        fields = ('number', 'label',)


class DataLabelSerializer(serializers.ModelSerializer):
    class Meta:
        model = ChartDataLabel
        fields = ('date', 'label',)


class ChartSerializer(serializers.ModelSerializer):
    serieslabels = SeriesLabelSerializer(many=True)
    datalabels = DataLabelSerializer(many=True)

    class Meta:
        model = Chart
        fields = ('csv', 'vertical_label', 'horizontal_label', 'serieslabels', 'datalabels')


class SlideSerializer(serializers.ModelSerializer):
    chart = ChartSerializer()

    class Meta:
        model = Slide
        fields = ('title', 'type', 'media', 'chart')

SlideSerializer 中使用chart = ChartSerializer(many=True) 输出:

...

{
    "title": "Slide 3",
    "type": "chart",
    "media": null,
    "chart": [
        {
            "csv": "/media/commodities/Hall_of_Fame_Inductees.csv",
            "vertical_label": "Vert",
            "horizontal_label": "Horiz",
            "serieslabels": [
                {
                    "number": "1",
                    "label": "One"
                },
                {
                    "number": "2",
                    "label": "Two"
                },
                {
                    "number": "3",
                    "label": "Three"
                }
            ],
            "datalabels": [
                {
                    "date": "2015-07-22",
                    "label": "This"
                },
                {
                    "date": "2015-07-23",
                    "label": "That"
                },
                {
                    "date": "2015-07-31",
                    "label": "The other"
                }
            ]
        }
    ]
},

...

请注意,chart 是一个 JSON 列表,表明它可以是许多图表

chart = ChartSerializer()SlideSerializer 中的预期输出:

...

{
    "title": "Slide 3",
    "type": "chart",
    "media": null,
    "chart": {
        "csv": "/media/commodities/Hall_of_Fame_Inductees.csv",
        "vertical_label": "Vert",
        "horizontal_label": "Horiz",
        "serieslabels": [
            {
                "number": "1",
                "label": "One"
            },
            {
                "number": "2",
                "label": "Two"
            },
            {
                "number": "3",
                "label": "Three"
            }
        ],
        "datalabels": [
            {
                "date": "2015-07-22",
                "label": "This"
            },
            {
                "date": "2015-07-23",
                "label": "That"
            },
            {
                "date": "2015-07-31",
                "label": "The other"
            }
        ]
    }
},

...

但它会抛出上面的错误。

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    您编写的所有内容都运行良好。处理嵌套表示应该是一个项目列表,您应该将many=True 标志传递给嵌套序列化程序。访问 http://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects

    所以你需要通过:

    chart = ChartSerializer(many=True)
    

    【讨论】:

    • 我明白,但是,正如我所提到的,只有一张图表与一张幻灯片相关联。所以many=True 没有意义。图表属性应表示为对象,而不是列表。列表适用于“许多”图表,对象适用于一个图表。
    • @tsantor 在您的模型幻灯片中,图表模型中有 FK 关系。如果我们看一下反向关系,一张幻灯片会有很多图表。所以我们需要将 many=True 传递给charserializer。
    • 也许这是我的困惑。对我来说,图表模型意味着图表属于(一个)幻灯片。这不是多对多领域。使用 OneToOne 字段返回单个对象(而不是列表)会更好吗?
    • 根据您的模型 FK 关系查看您需要在序列化程序中传递 many=True 。如果您想要单个对象,最好在模型中使用 OneTone 字段。
    【解决方案2】:

    我有同样的问题,即使我写了 many=False,它也会返回一个对象的列表。我认为原因是通过 filter() 查询访问子对象。因此它默认使用 many=True


    我通过使用 SerializerMethodField 解决了它。

    例如:

    class SlideSerializer(serializers.ModelSerializer):
        chart = serializers.SerializerMethodField()
    
        class Meta:
            model = Slide
            fields = ('title', 'type', 'media', 'chart')
        def get_chart(self, obj):
            return ChartSerializer(obj.chart).data
    

    【讨论】:

      猜你喜欢
      • 2021-09-20
      • 2013-07-05
      • 2015-05-15
      • 1970-01-01
      • 2015-04-29
      • 2018-12-15
      • 1970-01-01
      • 1970-01-01
      • 2022-11-29
      相关资源
      最近更新 更多