如何使用 Django Rest Framework 序列化程序构建我的 JSON 响应?

ian*_*oad 5 python django serialization django-rest-framework

我有一个数据库表:

servicenumber | meternumber | usagedatetime | usage
11111         | 22222       | 2019-01-01    | 1.85
11111         | 22222       | 2019-01-02    | 2.25
11111         | 22222       | 2019-01-03    | 1.55
11111         | 22222       | 2019-01-04    | 2.15
11111         | 33333       | 2019-02-01    | 2.95
11111         | 33333       | 2019-02-02    | 3.95
11111         | 33333       | 2019-02-03    | 2.05
11111         | 33333       | 2019-02-04    | 3.22
Run Code Online (Sandbox Code Playgroud)

如您所见,一个服务编号可以与多个仪表编号相关联。将服务编号视为不变的地理位置的唯一标识符。

我有一个 Django 模型:

class MeterUsage(models.Model):

    objectid = models.IntegerField(
        db_column='OBJECTID', unique=True, primary_key=True)

    servicenumber = models.IntegerField(
        db_column='serviceNumber', blank=True, null=True)

    meternumber = models.IntegerField(
        db_column='meterNumber', blank=True, null=True)

    usagedatetime = models.DateTimeField(
        db_column='usageDateTime', blank=True, null=True)

    usage = models.DecimalField(
        max_digits=38, decimal_places=8, blank=True, null=True)
Run Code Online (Sandbox Code Playgroud)

我有一个基本的序列化程序:

class MeterUsageSerializer(serializers.ModelSerializer):

    class Meta:
        model = MeterUsage
        fields = (
            'usagedatetime',
            'usage'
        )
Run Code Online (Sandbox Code Playgroud)

目前的回应是:

[
    {
        "usagedatetime": "2019-01-01",
        "usage": "1.85"
    },
    {
        "usagedatetime": "2019-01-02",
        "usage": "2.25"
    },
    {
        "usagedatetime": "2019-01-03",
        "usage": "1.55"
    },

    ....
]
Run Code Online (Sandbox Code Playgroud)

但我真正想要的是(需要用米号分隔的用法):

[
  {
    "servicenumber": "11111",
    "meternumber": "22222",
    "usagedata": [
      {
        "usagedatetime": "2019-01-01",
        "usage": "1.85"
      },
      {
        "usagedatetime": "2019-01-02",
        "usage": "2.25"
      },
      {
        "usagedatetime": "2019-01-03",
        "usage": "1.55"
      },
      {
        "usagedatetime": "2019-01-04",
        "usage": "2.15"
      },

      ...
    ]
  },
  {
    "servicenumber": "11111",
    "meternumber": "33333",
    "usagedata": [
      {
        "usagedatetime": "2019-02-01",
        "usage": "2.95"
      },
      {
        "usagedatetime": "2019-02-02",
        "usage": "3.95"
      },
      {
        "usagedatetime": "2019-02-03",
        "usage": "2.05"
      },
      {
        "usagedatetime": "2019-02-04",
        "usage": "3.22"
      },

      ...
    ]
  },

  ...
]
Run Code Online (Sandbox Code Playgroud)

我的最终目标是使用 Angular 7 应用程序中的 ChartJS 库在折线图中显示这些数据,并且数据需要采用以下格式:

  chartData = [
    {
      label: '22222',
      data: [1.85, 2.25, 1.55, 2.15] 
    },
    { 
      label: '33333',
      data: [2.95, 3.95, 2.05, 3.22]
    }
  ];
Run Code Online (Sandbox Code Playgroud)

是否可以使用序列化程序来格式化我上面显示的数据?我根据我读过的各种教程尝试了不同的技术,但似乎没有任何效果,现在我只是对如何处理它感到困惑。

任何见解表示赞赏!

Ozg*_*ali 3

如果您不打算更改模型结构,则无法使用 ModelSerializer 执行您想要的操作。使用 ModelSerializer,您可以在每个数据库行的结果列表中获得 1 个项目,但您希望将多行合并为结果列表中的单个项目。但是,您可以尝试选择唯一的 servicenumner -meternumber 对并直接子类Serializer来序列化您的数据。

在您看来:

queryset = MeterUsage.objects.values('servicenumber', 'meternumber').distinct()
return Response(MeterUsageSerializer(queryset, many=True).data)
Run Code Online (Sandbox Code Playgroud)

还有你的串行器:

class MeterUsageSerializer(serializers.Serializer):
    servicenumber = serializers.IntegerField()
    meternumber = serializers.IntegerField()
    usagedata = serializer.SerializerMethodField()

    def get_usagedata(self, obj):
        return [{
            'usagedatetime': item.usagedatetime.strftime('%Y-%m-%d'),
            'usage': item.usage
        } for item in MeterUsage.objects.filter(servicenumber=obj['servicenumber'], meternumber=obj['meternumber'])]
Run Code Online (Sandbox Code Playgroud)

如果您可以更改模型结构,则可以按以下方式构建模型和序列化器:

class MeterUsage(models.Model):

    objectid = models.IntegerField(
        db_column='OBJECTID', unique=True, primary_key=True)

    servicenumber = models.IntegerField(
        db_column='serviceNumber', blank=True, null=True)

    meternumber = models.IntegerField(
        db_column='meterNumber', blank=True, null=True)

    class Meta:
        unique_together = ('servicenumber', 'meternumber')


class MeterUsageData(models.Model):

    meterusage = models.ForeignKey(MeterUsage, on_delete=models.CASCADE, related_name='data')

    usagedatetime = models.DateTimeField(
        db_column='usageDateTime', blank=True, null=True)

    usage = models.DecimalField(
        max_digits=38, decimal_places=8, blank=True, null=True)

class MeterUsageDataSerializer(serializers.ModelSerializer):
    usagedatetime = serializers.DateTimeField(format=''%Y-%m-%d'')

    class Meta:
        model = MeterUsageData
        fields = ('usagedatetime', 'usage')

class MeterUsageSerializer(serializers.ModelSerializer):
    data = MeterUsageDataSerializer(many=True)

    class Meta:
        model = MeterUsage
        fields = ('servicenumber', 'meternumber', 'data')
Run Code Online (Sandbox Code Playgroud)

使用此模型 - 序列化器结构,您可以在视图中获得如下输出:

queryset = MeterUsage.objects.filter(...)
return Response(MeterUsageSerializer(queryset, many=True).data)
Run Code Online (Sandbox Code Playgroud)

请注意,使用这两种方法时,您都会对每个服务编号 - 计量编号对发出数据库查询。