Я слышал о нетерпеливых отношениях по загрузке данных в прошлом, но только недавно я стал более осознанным в моем 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”