Рубрики
Без рубрики

Стремление погрузка против ленивых погрузки в SQLALCHEMY

Я слышал о нетерпеливой загрузке данных в прошлом, но это было только недавно, что я б … Теги с SQL, Python, SQLalchemy.

Я слышал о нетерпеливых отношениях по загрузке данных в прошлом, но только недавно я стал более осознанным в моем API. Я покажу вам, ребята, что это такое и почему вы должны беспокоить об этом в этом посте.

Предварительные условия

Предпосылки для этого поста – это базовое понимание SQL Joins, Sqlalchemy и Python

Определения нетерпеливой нагрузки против ленивой загрузки

Стремительная загрузка – это методика извлечения данных о взаимопонимании модели при запросе модели через присоединение или подзапрос.

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

Я думаю, что примеры помогут сделать этот яснее.

Примеры

Скажем, у нас есть три модели, а именно пользователя, членство и компания, такое, что между пользователями и компаниями существует многие ко многим.

В моделях SQLalchemy:

class User(db.Model):
   id = db.Column(db.Integer, primary_key=True, autoincrement=True)
   username = db.Column(
     db.String(20),
     nullable=False,
   )
   password_hash = db.Column(db.VARCHAR(130), nullable=False)
   memberships = db.relationship('Membership',
                                 back_populates='member')


class Company(db.Model):
   id = db.Column(db.Integer, primary_key=True, autoincrement=True)
   name = db.Column(db.String(120), nullable=False)
   website = db.Column(db.String(), nullable=False)
   address = db.Column(db.String(), nullable=False)
   memberships = db.relationship('Membership',
                                 back_populates='company')


class Membership(db.Model):
   id = db.Column(db.Integer, primary_key=True, autoincrement=True)
   user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
   company_id = db.Column(db.Integer,
                               db.ForeignKey('company.id'))
   role = db.Column(db.String, default='REGULAR_USER')
   member = db.relationship("User", back_populates="memberships")
   company = db.relationship('Company', back_populates="memberships")

Ленивая проблема

Теперь, если мы запрашиваем членство в нашей базе данных, вроде так:

>>> membership = Membership.query.first()

По умолчанию SQLALCHEMY извлекает только данные, содержащиеся в таблице членства, запустив следующие запросы SQL:

INFO:sqlalchemy.engine.base.Engine:SELECT membership.id AS membership_id, membership.user_id AS membership_user_id, membership.company_id AS membership_company_id, membership.role AS membership_role
FROM membership
 LIMIT %(param_1)s

Теперь, когда вы пытаетесь получить доступ к Член. ИЗМЕНЕНИЕ содержащийся в объекте членства:

>>> membership.member.username

Это делает еще один призыв DB для извлечения элемента из таблицы пользователя, которая затем содержит имя пользователя:

INFO:sqlalchemy.engine.base.Engine:SELECT "user".id AS user_id, "user".username AS user_username, "user".password_hash AS user_password_hash
FROM "user"
WHERE "user".id = %(param_1)s
INFO:sqlalchemy.engine.base.Engine:{'param_1': 1}

Точно так же, когда мы пытаемся получить Имя из Компания , это приводит к другому Call db:

>>> membership.company.name
INFO:sqlalchemy.engine.base.Engine:SELECT company.id AS company_id, company.name AS company_name, company.website AS company_website, company.address AS company_address
FROM company
WHERE company.id = %(param_1)s
INFO:sqlalchemy.engine.base.Engine:{'param_1': 1}

Когда вы извлекаете несколько строк (скажите 50) из таблицы, используя ленивую загрузку, и вы запускаете петлю для доступа к связанному полю, SQLALCHEMY выделяет выбора операторов для каждого из (50) моделей, которые вы извлекли.

Обратите внимание, что зацикливание через каждую модель, описанную здесь, так как зефир сбрасывает вложенные поля в JSON.

Это ленивые полей нагрузочных целей работают. Вы пытаетесь получить данные только тогда, когда они нуждаются в них.

Нетерпеливый раствор

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

Мы делаем это с помощью sqlalchemy.orm Модуль вроде так:

>>> from sqlalchemy import orm
>>> Membership.query.options(orm.joinedload('company')).first()

Это генерирует следующие SQL:

INFO:sqlalchemy.engine.base.Engine:SELECT membership.id AS membership_id, membership.user_id AS membership_user_id, membership.company_id AS membership_company_id, membership.role AS membership_role, company_1.id AS company_1_id, company_1.name AS company_1_name, company_1.website AS company_1_website, company_1.address AS company_1_address
FROM membership LEFT OUTER JOIN company AS company_1 ON company_1.id = membership.company_id
 LIMIT %(param_1)s
INFO:sqlalchemy.engine.base.Engine:{'param_1': 1}

Как вы можете видеть, чтобы присоединиться, чтобы получить Компания Таким образом, отношения пытаются получить доступ к названию компании

>>> membership.company.name
'Ade Store1'

не приводит к любому дополнительным вызовам БД.

Стремление загрузки нескольких полей

Что если мы хотели вернуть вложенные данные с обоих Компания и Член Поля, как мы стремимся нагрузке оба? Мы можем сделать это легко, добавив больше аргументов на вызов метода опций, как так:

membership = Membership.query.options(
    orm.joinedload('company'), orm.joinedload('member')
).first()

Это генерирует следующее утверждение SQL:

INFO:sqlalchemy.engine.base.Engine:SELECT membership.id AS membership_id, membership.user_id AS membership_user_id, membership.company_id AS membership_company_id, membership.role AS membership_role, user_1.id AS user_1_id, user_1.username AS user_1_username, user_1.password_hash AS user_1_password_hash, company_1.id AS company_1_id, company_1.name AS company_1_name, company_1.website AS company_1_website, company_1.address AS company_1_address
FROM membership LEFT OUTER JOIN "user" AS user_1 ON user_1.id = membership.user_id LEFT OUTER JOIN company AS company_1 ON company_1.id = membership.company_id
 LIMIT %(param_1)s
INFO:sqlalchemy.engine.base.Engine:{'param_1': 1}

С этим доступен либо Компания или Член Поле не приводит к выполнению любого дополнительного оператора SQL.

Вынос

Вы должны стремиться к невольным полям отношения только тогда, когда вы знаете, что эти поля будут использоваться в вашем коде, что вы попадете в ситуацию, когда вы получаете значения, вам на самом деле не нужно, что может сделать ваш API немного медленнее. Кроме того, обязательно избегайте полей Lazy-loading, которые вам понадобится для вашей логики

Еще одна вещь отметить, что эта концепция не связана только к SQLALCHEMY. Он также существует в Django Orm и некоторых других не-питоновских ОРМ.

Вы можете получить больше информации на эту тему из SQLALCHEMY DOCS.

Заключение

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

Упрощение моделей SQLALCHEMY, создавая базовыйodel

Chidiebere Ogujeiofor · 12 февраля ’20 · 5 мин читать

Наконец, вы можете просматривать весь код (включая настройку), можно получить гейст ниже:

Спасибо за ваше время

Оригинал: “https://dev.to/chidioguejiofor/eager-loading-vs-lazy-loading-in-sqlalchemy-5209”