【问题标题】:Django REST serialize field group by dateDjango REST按日期序列化字段组
【发布时间】:2021-03-07 18:11:02
【问题描述】:

问题:无法按日期对 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 的解决方案:

views.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

【问题讨论】:

    标签: django django-models django-rest-framework django-orm


    【解决方案1】:

    你可以这样做

    让我们先从你的观点开始,我会像这样调整查询集

    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导致,可能还有另一种方法可以在一次数据库命中中做到这一点,如果我会更新我的答案想通了

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

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

    【讨论】:

    • 您好 Mahnoud,感谢您抽出宝贵时间提供帮助。我尝试了你的建议,这让我遇到了这个错误:django.db.utils.NotSupportedError: DISTINCT ON fields is not supported by this database backend 在网络上的一些提示之后,我尝试将视图更改为:workout_log = WorkoutLog.objects.order_by('date').values_list('date', flat=True).distinct(),但随后出现此错误。 AttributeError The serializer field might be named incorrectly and not match any attribute or key on the 'date' instance. Original exception text was: 'datetime.date' object has no attribute 'date'.
    • 这是因为flat=True 而发生的,因为它只会返回数据并将其映射到字段名称date,这就是序列化程序也会引发属性错误的原因,删除flat=true 它应该可以工作, workout_log = WorkoutLog.objects.order_by('date').values_list('date').distinct()
    • 所以我现在尝试了不同的方法,但不会走运。当我使用workout_log = WorkoutLog.objects.order_by('date').values_list('date').distinct() 时,我会得到错误:“原始异常文本是:'tuple' 对象没有属性'date”
    • 如果我使用workout_log = WorkoutLog.objects.order_by('date').values('date').distinct() 我会得到错误:“workouts = WorkoutLog.objects.filter(date=obj.date) AttributeError: 'dict' object has no attribute 'date'”
    • 非常感谢您抽出宝贵时间帮助并正确解释一切。它现在起作用了!非常感谢!我用了至少 5 个小时试图弄清楚。我 4 周前刚开始学习 Django,所以学习速度很慢……再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-09
    相关资源
    最近更新 更多