Колба много ко многим присоединяется как сделано prefetch_related из django - PullRequest
0 голосов
/ 05 января 2019

У меня есть следующая группа и модель контакта в колбе с 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')

Теперь мне нужно запросить контакт с группами:

contacts = Contact.query.join(Group, Contact.groups).all()
for contact in contacts:
    print(contact.groups)

Здесь проблема в том, что количество запросов SQL увеличивается с увеличением числа контактов, когда я выполняю приведенный выше код.

Django ORM имеет prefetch_related () с набором запросов, который выполняет следующие действия в соответствии с 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]))

Но это дает мне эту ошибку:

AttributeError: 'Table' object has no attribute 'query'

Как я могу получить prefetch_related, как функцию от django с SqlAlchemy?

1 Ответ

0 голосов
/ 05 января 2019

Вы хотите сказать SQLAlchemy, чтобы с нетерпением загружать связанные объекты, используя технику загрузки отношений . SQLAlchemy можно сказать загружать группы вместе с контактами в одном запросе.

Только для этого одного запроса вы можете добавить joinedload() параметр (он доступен через объект Flask-SQLAlchemy db):

contacts = Contact.query.options(db.joinedload(Contact.groups)).all()

Это предварительно загружает атрибут Contact.groups на каждый соответствующий контакт:

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

Выполненный запрос выглядит следующим образом:

SELECT 
    contact.id AS contact_id,
    contact.phone AS contact_phone,
    group_1.id AS group_1_id,
    group_1.name AS group_1_name
FROM contact 
LEFT OUTER JOIN (
    group_contact AS group_contact_1
    JOIN "group" AS group_1 ON group_1.id = group_contact_1.group_id
) ON contact.id = group_contact_1.contact_id

Вы также можете установить стратегию загрузки по умолчанию для отношений в модели; чтобы всегда охотно загружать группы, используйте lazy='joined' в отношении:

class Contact(db.Model):
    # ...
    groups = db.relationship(
        "Group", secondary=group_contact, backref='contacts',
        lazy='joined')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...