按两个值分组并获得第三个值
group by two values and get the third values
我有一个包含三个 CharField 的 Django 模型,我想 运行 对其进行查询以获取它们两个的现有值,并为每个组合获取第三个字段的现有值。
a = models.CharField(null=False, max_length=8000)
b = models.CharField(null=False, max_length=8000)
c = models.CharField(null=False, max_length=8000)
如果假设这些值在数据库中:
a | b | c |
---------------
a1 | b2 | c3 |
a1 | b2 | c1 |
a2 | b2 | c3 |
a1 | b3 | c3 |
a1 | b2 | c2 |
我想要这种形式的结果:
{"a1-b2" : [c3, c1, c2], "a2-b2" : [c3], "a1-b3" : [c3]}
or
{"a1" : {"b2":[c3, c1, c2], "b3": [c3]}, "a2": {"b2" : [c3]}}
想不出好的纯 SQL 解决方案,但这是使用 groupby
:
的 pythonic 解决方案
from itertools import groupby
# Order by key fields so it will be easier to group later
items = YOUR_MODEL.objects.order_by('a', 'b')
# Group items by 'a' and 'b' fields as key
groups = groupby(items, lambda item: (item.a, item.b))
# Create dictionary with values as 'c' field from each item
res = {
'-'.join(key): list(map(lambda item: item.c, group))
for key, group in groups
}
# {'a1-b2': ['c3', 'c1', 'c2'], 'a1-b3': ['c3'], 'a2-b2': ['c3']}
TLDR:
items = MyModel.objects.annotate(custom_field=Concat('a', Values('-'), 'b').values('custom_field', 'c')
说明
对于 .annotate(custom_field=Concat('a', Values('-'), 'b')
部分,您基本上是在 SQL 中执行 group_by 操作,并在您的查询集中创建一个名称为 custom_field
的临时新列,它将具有a-b
.
的值
这为您提供了以下结构:
a | b | c | custom_field
a1 b1 c1 a1-b1
a2 b2 c2 a2-b2
a1 b1 c3 a1-b1
.values('custom_field', 'c')
部分仅从该查询集中提取 custom_field
和 c
列。现在您所要做的就是序列化您的数据。
编辑
如果您想要特定格式的数据,您可以连接列 c
。请阅读此 post 中 SO 接受的答案。 。然后,您可以在序列化期间创建一个新字段,它将 split()
串联的 c
字段放入列表中。
我有一个包含三个 CharField 的 Django 模型,我想 运行 对其进行查询以获取它们两个的现有值,并为每个组合获取第三个字段的现有值。
a = models.CharField(null=False, max_length=8000)
b = models.CharField(null=False, max_length=8000)
c = models.CharField(null=False, max_length=8000)
如果假设这些值在数据库中:
a | b | c |
---------------
a1 | b2 | c3 |
a1 | b2 | c1 |
a2 | b2 | c3 |
a1 | b3 | c3 |
a1 | b2 | c2 |
我想要这种形式的结果:
{"a1-b2" : [c3, c1, c2], "a2-b2" : [c3], "a1-b3" : [c3]}
or
{"a1" : {"b2":[c3, c1, c2], "b3": [c3]}, "a2": {"b2" : [c3]}}
想不出好的纯 SQL 解决方案,但这是使用 groupby
:
from itertools import groupby
# Order by key fields so it will be easier to group later
items = YOUR_MODEL.objects.order_by('a', 'b')
# Group items by 'a' and 'b' fields as key
groups = groupby(items, lambda item: (item.a, item.b))
# Create dictionary with values as 'c' field from each item
res = {
'-'.join(key): list(map(lambda item: item.c, group))
for key, group in groups
}
# {'a1-b2': ['c3', 'c1', 'c2'], 'a1-b3': ['c3'], 'a2-b2': ['c3']}
TLDR:
items = MyModel.objects.annotate(custom_field=Concat('a', Values('-'), 'b').values('custom_field', 'c')
说明
对于 .annotate(custom_field=Concat('a', Values('-'), 'b')
部分,您基本上是在 SQL 中执行 group_by 操作,并在您的查询集中创建一个名称为 custom_field
的临时新列,它将具有a-b
.
这为您提供了以下结构:
a | b | c | custom_field
a1 b1 c1 a1-b1
a2 b2 c2 a2-b2
a1 b1 c3 a1-b1
.values('custom_field', 'c')
部分仅从该查询集中提取 custom_field
和 c
列。现在您所要做的就是序列化您的数据。
编辑
如果您想要特定格式的数据,您可以连接列 c
。请阅读此 post 中 SO 接受的答案。 split()
串联的 c
字段放入列表中。