>>> На главную <<<

Sqlalchgemy querying

основная статья

Создается queryобъект с помощью метода query() в сессии. Этот метод получает аргументы, которыми можно манипулировать над классами и дескрипторами.

А примере мы получаем инстансы юзеров, корторе можно итерировать

>>> for instance in session.query(User).order_by(User.id):
...     print(instance.name, instance.fullname)
ed Ed Jones
wendy Wendy Williams
mary Mary Contrary
fred Fred Flintstone

Когда мы запрашиваем несколько объектов, базирусяь на колонках, возвращается кортеж

>>> for name, fullname in session.query(User.name, User.fullname):
...     print(name, fullname)
ed Ed Jones
wendy Wendy Williams
mary Mary Contrary
fred Fred Flintstone

query возвращает named tuples object, ключи совпадают с именами ОРМ-бейсед объектов

>>> for row in session.query(User, User.name).all():
...    print(row.User, row.name)
<User(name='ed', fullname='Ed Jones', nickname='eddie')> ed
<User(name='wendy', fullname='Wendy Williams', nickname='windy')> wendy
<User(name='mary', fullname='Mary Contrary', nickname='mary')> mary
<User(name='fred', fullname='Fred Flintstone', nickname='freddy')> fred

Ключ можно изменить

>>> for row in session.query(User.name.label('name_label')).all():
...    print(row.name_label)
ed
wendy
mary
fred

Имена классов можно контроллировать через алияс

>>> from sqlalchemy.orm import aliased
>>> user_alias = aliased(User, name='user_alias')

SQL>>> for row in session.query(user_alias, user_alias.name).all():
...    print(row.user_alias)
<User(name='ed', fullname='Ed Jones', nickname='eddie')>
<User(name='wendy', fullname='Wendy Williams', nickname='windy')>
<User(name='mary', fullname='Mary Contrary', nickname='mary')>
<User(name='fred', fullname='Fred Flintstone', nickname='freddy')>

Можно задавать порядок выдачи

>>> for u in session.query(User).order_by(User.id)[1:3]:
...    print(u)
<User(name='wendy', fullname='Wendy Williams', nickname='windy')>
<User(name='mary', fullname='Mary Contrary', nickname='mary')>

или фильтрацию

>>> for name, in session.query(User.name).\
...             filter_by(fullname='Ed Jones'):
...    print(name)
ed

или так

>>> for name, in session.query(User.name).\
...             filter(User.fullname=='Ed Jones'):
...    print(name)
ed

можно выстраивать цепочки

>>> for user in session.query(User).\
...          filter(User.name=='ed').\
...          filter(User.fullname=='Ed Jones'):
...    print(user)
<User(name='ed', fullname='Ed Jones', nickname='eddie')>

Все базовые операции фильтрации тут

query.all() возвращает список. Query.first() возвращает скаляр. Есть еще Query.one() Смотреть тут

Кроме того, sql-запросы можно использовать в виде текста

Querying guide

основная статья тут

SELECT statements

Задаем select() объект, отправляем его в сессию и получаем результат

>>> from sqlalchemy import select
>>> stmt = select(User).where(User.name == 'spongebob')

>>> result = session.execute(stmt)
>>> for user_obj in result.scalars():
...     print(f"{user_obj.name} {user_obj.fullname}")
spongebob Spongebob Squarepants

select() поддерживает ОРМ модели, включая маппированные классы, такие как атрибуты уровня класса, реализующие колонки таблицы. Работа с такой аннотацией так-же осуществляется в сесссии.

>>> result = session.execute(select(User).order_by(User.id))

При оспользовании ОРМ-мода, названия сущностей базируются на поименовании классов

>>> stmt = select(User, Address).join(User.addresses).order_by(User.id, Address.id)

SQL>>> for row in session.execute(stmt):
...    print(f"{row.User.name} {row.Address.email_address}")
spongebob spongebob@sqlalchemy.org
sandy sandy@sqlalchemy.org
sandy squirrel@squirrelpower.org
patrick pat999@aol.com
squidward stentcl@sqlalchemy.org

Атрибуты классов можно использовать и так:

>>> result = session.execute(
...     select(User.name, Address.email_address).
...     join(User.addresses).
...     order_by(User.id, Address.id)
... )

>>> for row in result:
...     print(f"{row.name}  {row.email_address}")
spongebob  spongebob@sqlalchemy.org
sandy  sandy@sqlalchemy.org
sandy  squirrel@squirrelpower.org
patrick  pat999@aol.com
squidward  stentcl@sqlalchemy.org

В данном случае под капотом другая реализация, итог тот же. Подробнее

Тоже самое можно собрать через банедлы, но есть потенциальный риск наткнуться на тяжелый запрос. Доп.инфа по column bundles

>>> from sqlalchemy.orm import Bundle
>>> stmt = select(
...     Bundle("user", User.name, User.fullname),
...     Bundle("email", Address.email_address)
... ).join_from(User, Address)
SQL>>> for row in session.execute(stmt):
...     print(f"{row.user.name} {row.email.email_address}")
spongebob spongebob@sqlalchemy.org
sandy sandy@sqlalchemy.org
sandy squirrel@squirrelpower.org
patrick pat999@aol.com
squidward stentcl@sqlalchemy.org

Можно использовать алиасы

>>> from sqlalchemy.orm import aliased
>>> u1 = aliased(User, name="u1")
>>> stmt = select(u1).order_by(u1.id)
SQL>>> row = session.execute(stmt).first()
>>> print(f"{row.u1.name}")
spongebob

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

Joins

Select.join() и Select.join_from() предпочтительнее в ОРМ 2.0, чем Query.join(), который является легаси. Далее простой пример джойна. В данном случае реализована связь между двумя классами User и Adress, ult User.adresses представляет коллекцию адресов, ассоциированных с юзером.

>>> stmt = select(User).join(User.addresses)
>>> print(stmt)
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account JOIN address ON user_account.id = address.user_id

Множественные джойны можно выстракивать в цепь

>>> stmt = (
...     select(User).
...     join(User.orders).
...     join(Order.items)
... )
>>> print(stmt)
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
JOIN user_order ON user_account.id = user_order.user_id
JOIN order_items AS order_items_1 ON user_order.id = order_items_1.order_id
JOIN item ON item.id = order_items_1.item_id

Второй вариант - джойны на основе самого класса. Такой подход потенциально опасен ошибками, которые будут подниматься, если не установлен ForeignKeyConstraint или их несколько. Подробнее

>>> stmt = select(User).join(Address)
>>> print(stmt)
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account JOIN address ON user_account.id = address.user_id

Третий вариант - реализовать фразу ON

>>> stmt = select(User).join(Address, User.id==Address.user_id)
>>> print(stmt)
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account JOIN address ON user_account.id = address.user_id

Можно так:

>>> stmt = select(User).join(Address, User.addresses)
>>> print(stmt)

Такой синтаксис становится более полезным с алиасами

>>> a1 = aliased(Address)
>>> a2 = aliased(Address)
>>> stmt = (
...     select(User).
...     join(a1, User.addresses).
...     join(a2, User.addresses).
...     where(a1.email_address == 'ed@foo.com').
...     where(a2.email_address == 'ed@bar.com')
... )
>>> print(stmt)
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
JOIN address AS address_1 ON user_account.id = address_1.user_id
JOIN address AS address_2 ON user_account.id = address_2.user_id
WHERE address_1.email_address = :email_address_1
AND address_2.email_address = :email_address_2

Можно тоже самое через of_type() метод

>>> stmt = (
...     select(User).
...     join(User.addresses.of_type(a1)).
...     join(User.addresses.of_type(a2)).
...     where(a1.email_address == 'ed@foo.com').
...     where(a2.email_address == 'ed@bar.com')
... )
>>> print(stmt)

Цепи можно реализовать на лету через метод and_()

>>> stmt = (
...     select(User).
...     join(User.addresses.and_(Address.email_address != 'foo@bar.com'))
... )
>>> print(stmt)
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
JOIN address ON user_account.id = address.user_id
AND address.email_address != :email_address_1

Можно джониться к сабзапросам. Подробнее тут

Смотри также ORM execution options

>>> На главную <<<