如何通过 Django 中的模型属性之一对对象数组进行分组?
Posted
技术标签:
【中文标题】如何通过 Django 中的模型属性之一对对象数组进行分组?【英文标题】:How to group an array of objects by one of the model's attribute in Django? 【发布时间】:2021-08-09 18:11:11 【问题描述】:我想做类似于此示例 How to group an array of objects by key 的操作,但我在 Django 中找不到 API Get 的解决方案。
这是我所拥有的一个例子:
[
"id": 1,
"nutrition_measurement_id": "1",
"nutrition_type_id": "2",
"feeding_time": "dinner"
,
"id": 2,
"nutrition_measurement_id": "2",
"nutrition_type_id": "1",
"feeding_time": "dinner"
,
"id": 3,
"nutrition_measurement_id": "3",
"nutrition_type_id": "2",
"feeding_time": "breakfast"
,
"id": 4,
"nutrition_measurement_id": "2",
"nutrition_type_id": "1",
"feeding_time": "breakfast"
]
这是我想要实现的示例:
"dinner": [
"id": 3,
"nutrition_type_id": 2,
"nutrition_measurement_id": 1
,
"id": 3,
"nutrition_type_id": 1,
"nutrition_measurement_id": 2
],
"breakfast": [
"id": 3,
"nutrition_type_id": 2,
"nutrition_measurement_id": 3
,
"id": 5,
"nutrition_type_id": 1,
"nutrition_measurement_id": 4
]
这是我拥有的模型:
class HorseNutrition(models.Model):
horse = models.ForeignKey(Horse, models.DO_NOTHING)
nutrition_type = models.ForeignKey('NutritionType', models.DO_NOTHING)
nutrition_measurement = models.ForeignKey('NutritionMeasurement', models.DO_NOTHING)
feeding_time = models.CharField(max_length=10, blank=True, null=True)
class Meta:
managed = False
db_table = 'horse_nutrition'
class NutritionMeasurement(models.Model):
name = models.CharField(unique=True, max_length=30, blank=True, null=True)
class Meta:
managed = False
db_table = 'nutrition_measurement'
def __str__(self):
return self.name
def __unicode__(self):
return self.name
class NutritionType(models.Model):
name = models.CharField(unique=True, max_length=30, blank=True, null=True)
class Meta:
managed = False
db_table = 'nutrition_type'
def __str__(self):
return self.name
def __unicode__(self):
return self.name
这是我的序列化程序
class HorseNutritionSerializer(serializers.ModelSerializer):
nutrition_measurement_id = serializers.ReadOnlyField(source = 'nutrition_measurement.name')
nutrition_type_id = serializers.ReadOnlyField(source = 'nutrition_type.name')
class Meta:
model = HorseNutrition
fields = ['id', 'nutrition_measurement_id', 'nutrition_type_id', 'feeding_time']
这是我的看法:
class HorseNutritionView(APIView):
def get (self, request, format = None):
#id = int(request.GET.get(self.lookup_url_kwarg))
id = 1
if id != None:
queryset = HorseNutrition.objects.filter(horse_id = id)
serializer = HorseNutritionSerializer(queryset, many = True)
return Response(serializer.data)
else:
return Response(status = status.HTTP_400_BAD_REQUEST)
【问题讨论】:
【参考方案1】:您可以使用自定义ListSerializer
(我们将在您的序列化程序的Meta
中设置list_serializer_class
)并覆盖其to_representation
并使用itertools.groupby
对您的结果进行分组:
from itertools import groupby
class HorseNutritionListSerializer(serializers.ListSerializer):
def to_representation(self, data):
ret = super().to_representation(data)
return key: list(group) for key, group in groupby(ret, key=lambda x: x['feeding_time'])
@property
def data(self):
ret = serializers.BaseSerializer.data.fget(self)
return serializers.ReturnDict(ret, serializer=self)
class HorseNutritionSerializer(serializers.ModelSerializer):
nutrition_measurement_id = serializers.ReadOnlyField(source = 'nutrition_measurement.name')
nutrition_type_id = serializers.ReadOnlyField(source = 'nutrition_type.name')
class Meta:
model = HorseNutrition
fields = ['id', 'nutrition_measurement_id', 'nutrition_type_id', 'feeding_time']
list_serializer_class = HorseNutritionListSerializer
此外,要正确完成此分组,您的查询集必须是有序的,您可以在视图中执行此操作:
class HorseNutritionView(APIView):
def get (self, request, format = None):
#id = int(request.GET.get(self.lookup_url_kwarg))
id = 1
if id != None:
queryset = HorseNutrition.objects.filter(horse_id = id).order_by('feeding_time')
serializer = HorseNutritionSerializer(queryset, many = True)
return Response(serializer.data)
else:
return Response(status = status.HTTP_400_BAD_REQUEST)
【讨论】:
这很有意义,但我不知道为什么它不起作用:( @olczig 啊,我的错,查询集(当many=True
时)由父序列化程序 ListSerializer
在内部序列化,因此我们需要覆盖它的 to_representation
,我们将不得不使用它自定义ListSerializer
并将其设置为list_serializer_class
。检查我的编辑。
现在我只有喂食时间列表
@olczig 喂食时间列表?您应该得到一个 dictionary,其中包含馈送时间作为键和序列化 HorseNutrition
的列表作为值,因为这就是我们返回的内容。
@olczig 我从回答这个问题中学到了很多:),问题是ListSerializer.data
返回ReturnList(ret, serializer=self)
所以字典变成了一个列表。我也编辑了解决这个问题的答案(也许在视图中使用itertools.groupby
会更容易,但你可以通过这种方式学习新东西;))以上是关于如何通过 Django 中的模型属性之一对对象数组进行分组?的主要内容,如果未能解决你的问题,请参考以下文章