由 django 中的 prefetch_related 完成的烧瓶多对多连接

wru*_*esh 3 python postgresql sqlalchemy

我在带有 Sql Alchemy ORM 的烧瓶中有以下组和联系人模型

group_contact = db.Table(
    'group_contact',
    db.Column('group_id', db.Integer, db.ForeignKey(
        'group.id')),
    db.Column('contact_id', db.Integer, db.ForeignKey(
        'contact.id')),
    db.PrimaryKeyConstraint('group_id', 'contact_id')
)


class Group(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))


class Contact(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    phone = db.Column(db.String(15), nullable=False, unique=True)
    groups = db.relationship(
        "Group", secondary=group_contact, backref='contacts')
Run Code Online (Sandbox Code Playgroud)

现在我需要查询与组的联系:

contacts = Contact.query.join(Group, Contact.groups).all()
for contact in contacts:
    print(contact.groups)
Run Code Online (Sandbox Code Playgroud)

这里的问题是当我执行上面的代码时,SQL 查询的数量随着联系数量的增加而增加。

Django ORM 具有 prefetch_related() 和 queryset,它根据 django docs执行以下操作。

另一方面,prefetch_related 对每个关系进行单独的查找,并在 Python 中进行“连接”。除了 select_related 支持的外键和一对一关系之外,这允许它预取使用 select_related 无法完成的多对多和多对一对象。

现在我正在尝试通过以下代码对 Sql Alchemy 做同样的事情:

contacts = Contact.query.all()     
contact_groups = group_contact.query.join(
    Group
).filter(group_contact.contact_id.in_([item.id for item in contacts]))
Run Code Online (Sandbox Code Playgroud)

但这给了我这个错误:

AttributeError: 'Table' object has no attribute 'query'
Run Code Online (Sandbox Code Playgroud)

如何使用 SqlAlchemy 从 django 获取 prefetch_related 之类的功能?

Mar*_*ers 5

您想告诉 SQLAlchemy 使用关系加载技术急切加载相关对象。可以告诉 SQLAlchemy 在单个查询中加载组和联系人。

对于这一个查询,您可以添加joinedload()选项(它可以通过 Flask-SQLAlchemydb对象获得):

contacts = Contact.query.options(db.joinedload(Contact.groups)).all()
Run Code Online (Sandbox Code Playgroud)

这会Contact.groups在每个匹配的联系人上预加载属性:

for contact in contacts:
    # no new query issued to fetch groups, the data for the groups
    # is already available
    print(contact.groups)
Run Code Online (Sandbox Code Playgroud)

执行的查询如下所示:

contacts = Contact.query.options(db.joinedload(Contact.groups)).all()
Run Code Online (Sandbox Code Playgroud)

您还可以为模型上的关系设置默认加载策略;总是急切地加载组,lazy='joined'在关系上使用:

class Contact(db.Model):
    # ...
    groups = db.relationship(
        "Group", secondary=group_contact, backref='contacts',
        lazy='joined')
Run Code Online (Sandbox Code Playgroud)