首页 > 解决方案 > Django REST按日期序列化字段组

问题描述

问题:无法按日期对 JSON 输出进行分组

本文底部的解决方案

我正在序列化一个模型并得到这个输出:

[
    {
        "date": "2020-11-24",
        "name": "Chest",
        "workout": {
            "name": "Chest",
            "exercise": 1,
            "repetitions": 10,
            "weight": 80
        }
    },
    {
        "date": "2020-11-24",
        "name": "Chest",
        "workout": {
            "name": "Chest",
            "exercise": 1,
            "repetitions": 10,
            "weight": 85
        }
    },
    {
        "date": "2020-11-24",
        "name": "Chest",
        "workout": {
            "name": "Chest",
            "exercise": 1,
            "repetitions": 10,
            "weight": 90
        }
    },

我想像下面的 JSON 一样获取它并按date分组。

[
    {
        "date": "2020-11-24",
        "workout": {
            "name": "Chest",
            "exercise": 1,
            "repetitions": 10,
            "weight": 80
        },
            "name": "Chest",
            "exercise": 1,
            "repetitions": 10,
            "weight": 85
        },
            "name": "Chest",
            "exercise": 1,
            "repetitions": 10,
            "weight": 90
        },
    }
]

我有一个模型:

class WorkoutLog(models.Model):
    date = models.DateField()
    name = models.CharField(max_length=50) #When save() name = Workout.name
    exercise = models.ForeignKey('Exercise', related_name='log', on_delete=models.CASCADE)
    repetitions = models.IntegerField()
    weight = models.IntegerField()

尝试按日期对 JSON 进行序列化和分组:

class WorkoutLogSerializer(serializers.ModelSerializer):
    class Meta:
        model = WorkoutLog
        fields = ['date', 'workout']
    
    workout = serializers.SerializerMethodField('get_workout')

    def get_workout(self, obj):
        return {
            'name': obj.name,
            'exercise': obj.exercise_id,
            'repetitions': obj.repetitions,
            'weight': obj.weight,
        }

该代码允许我自定义字段布局,但不能真正按日期对其进行分组。您对如何构建它有什么建议吗?

非常感谢大家的帮助!

如果需要,这是我的 view.py

def workout_log(request):
    if request.method == 'GET':
        workout_log = WorkoutLog.objects.all()
        serializer = WorkoutLogSerializer(workout_log, many=True)
        return JsonResponse(serializer.data, safe=False)

Mahmoud Adel 的解决方案:

视图.py

def workout_log(request):
    if request.method == 'GET':
        workout_log = WorkoutLog.objects.order_by('date').values('date').distinct()
        serializer = WorkoutLogSerializer(workout_log, many=True)
        return JsonResponse(serializer.data, safe=False)

序列化程序.py

class WorkoutFieldSerializer(serializers.Serializer):
      name = serializers.CharField()
      #exercise = serializers.IntegerField()
      repetitions = serializers.IntegerField()
      weight = serializers.IntegerField()
      
class WorkoutLogSerializer(serializers.ModelSerializer):
    class Meta:
        model = WorkoutLog
        fields = ['date', 'workout']
    
    workout = serializers.SerializerMethodField('get_workout')

    def get_workout(self, obj):
        workouts = WorkoutLog.objects.filter(date=obj['date'])
        workout_serializer = WorkoutFieldSerializer(workouts, many=True)
        return workout_serializer.data

标签: djangodjango-modelsdjango-rest-frameworkdjango-orm

解决方案


你可以做这样的事情

让我们首先从您的观点开始,我将像这样调整查询集

def workout_log(request):
    if request.method == 'GET':
        workout_log = WorkoutLog.objects.order_by('date').distinct('date').only('date')
        serializer = WorkoutLogSerializer(workout_log, many=True)
        return JsonResponse(serializer.data, safe=False)

然后在你的WorkoutLogSerializer

class WorkoutLogSerializer(serializers.ModelSerializer):
    class Meta:
        model = WorkoutLog
        fields = ['date', 'workout']
    
    workout = serializers.SerializerMethodField('get_workout')

    def get_workout(self, obj):
        workouts = WorkoutLog.objects.filter(date=obj.date)
        workout_serializer = WorkoutSerializer(workouts, many=True)
        return workout_serializer.data

最后,创建WorkoutSerializer

class WorkoutSerializer(serializers.Serializers):
      name = serializers.CharField()
      exercise = serializers.IntegerField()
      repetitions = serializers.IntegerField()
      weight = serializers.IntegerField()

前面的方法首先会到数据库中获取distinct日期,然后WorkoutLogSerializer将使用每个日期来选择具有它的相应对象,然后我们序列化这些对象。

我们这样做得到了预期的结果,请注意,这将导致2 DB hits可能有另一种方法可以在一个 DB 命中中做到这一点,如果我想通了,我会更新我的答案

注意:我写这篇文章是为了解释我没有运行它的流程和逻辑,虽然它应该可以工作,但我可能会忘记一些会显示错误的内容,请随时尝试。

更新:如果您使用 SQLite,请检查此答案评论。


推荐阅读