按父属性对对象列表进行分组,并将输出序列化到dict



设置:

我有三个SQLAlchemy类BusinessUnitTaskArea,它们处于一种关系中。

class BusinessUnit(db.Model)
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
db.relationship("Task", backref="business_unit")

class Task(db.Model)
id = db.Column(db.Integer, primary_key=True)
id_business_unit = db.Column(db.ForeignKey('business_unit.id'), nullable=False)
id_area = db.Column(db.ForeignKey('area.id'), nullable=False)
name = db.Column(db.String)
@property
def serialize():
return {
'id': self.id,
'name': self.name,
}

class Area(db.Model)
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)

首先,我查询任务并按区域进行筛选。

tasks = db.session.query(Task).join(Area).filter(Area.id == area).all()
print(tasks)
>>> [<Task 1>, <Task 2>, <Task 3>]

问题:

然后,结果应按业务单元进行序列化和分组。数据必须检索为dicts列表。输出示例如下:

serialized_tasks = [{
"business_unit_name": "Business Unit A",
"tasks": [{
"id": 1,
"name: "Task 1"
},
{
"id": 2,
"name: "Task 2"
}]
},
{
"business_unit_name": "Business Unit B",
"tasks": [{
"id": 3,
"name: "Task 3"
},
}]

我真的被卡住了,不知道如何得到想要的输出。不幸的是,我无法更改数据库模型,因此查询链接表(Task(似乎是唯一的方法。


我尝试了什么:

根据Bernhards在这篇文章中的回答,我得到了以下输出。它非常接近我所需要的,但不幸的是,这是一个包含列表的dict。

serialized_tasks= []
res = {}
for item in tasks:
res.setdefault(item.business_unit.name, []).append(item.serialize)
serialized_tasks.append(res)

print(serialized_tasks)
>>> {"Business Unit A": [{
"id": 1,
"name": "Task 1"
},
{
"id": 2,
"name": "Task 2"
}],
"Business Unit B": [{
"id": 3,
"name": "Task 3"
}]
}

每次调用res.setdefault(item.business_unit.name, [])时,都有可能创建一个不必要的列表,然后对其进行垃圾收集。您可以通过使用defaultdict:来避免这种情况

from collections import defaultdict
name_to_tasks = defaultdict(list)
for item in tasks:
name_to_tasks[item.business_unit.name].append(item.serialize)
result = [
{"business_unit_name": business_unit_name, "tasks": tasks_}
for business_unit_name, tasks_ in name_to_tasks.items()
]

然而,如果你不介意使用第三方库,这会增加一些糖,我建议使用convtools(我必须承认,我是作者(-github。

from convtools import conversion as c
# this step can be done once, so the converter is reused just like any other
# function
converter = (
c.group_by(c.attr("business_unit", "name"))
.aggregate(
{
"business_unit_name": c.attr("business_unit", "name"),
"tasks": c.ReduceFuncs.Array(c.attr("serialize")),
}
)
.gen_converter()
)
converter(tasks)

最新更新