В прототипном приложении, использующем Python и SQLAlchemy с базой данных PostgreSQL, я имею следующую схему (выдержку):
class Guest(Base):
__tablename__ = 'guest'
id = Column(Integer, primary_key=True)
name = Column(String(50))
surname = Column(String(50))
email = Column(String(255))
[..]
deleted = Column(Date, default=None)
Я хочу построить запрос, используя SQLAlchemy, который извлекает список гостей, которые будут отображаться в бэк-офисе.
Для реализации разбиения на страницы я буду использовать LIMIT и OFFSET, а также COUNT (*) OVER(), чтобы получить общее количество записей при выполнении запроса (не с другим запросом).
Примером SQL-запроса может быть:
SELECT id, name, surname, email,
COUNT(*) OVER() AS total
FROM guest
WHERE (deleted IS NULL)
ORDER BY id ASC
LIMIT 50
OFFSET 0
Если бы я должен был построить запрос с помощью SQLAlchemy, я мог бы сделать что-то вроде:
query = session.query(Guest)
query = query.filter(Login.deleted == None)
query = query.order_by(Guest.id.asc())
query = query.offset(0)
query = query.limit(50)
result = query.all()
И если бы я хотел считать все строки в таблице гостей, я мог бы сделать что-то вроде этого:
from sqlalchemy import func
query = session.query(func.count(Guest.id))
query = query.filter(Login.deleted == None)
result = query.scalar()
Теперь вопрос, который я задаю, заключается в том, как выполнить один единственный запрос, используя SQLAlchemy, аналогично тому, как это было выше, что убивает двух птиц одним камнем (возвращает первые 50 строк и количество полных строк для создания ссылок на страницы, все в одном запросе).
Интересным битом является использование оконных функций в PostgreSQL, что позволяет вышеназванное поведение, тем самым избавляя вас от необходимости запрашивать дважды, но только один раз.
Является ли это возможным?
Заранее спасибо.
Поэтому я не нашел примеров в документации SQLAlchemy, но нашел эти функции:
[count()][1]
[over()][1]
[label()][1]
И мне удалось совместить их, чтобы получить именно тот результат, который я искал:
from sqlalchemy import func
query = session.query(Guest, func.count(Guest.id).over().label('total'))
query = query.filter(Guest.deleted == None)
query = query.order_by(Guest.id.asc())
query = query.offset(0)
query = query.limit(50)
result = query.all()
Ура!
PS Я также нашел этот вопрос в Stack Overflow, который остался без ответа.
Вы можете запускать SQL непосредственно в SQLAlchemy:
engine = create_engine('postgresql://postgres:password@localhost:5432/yourdb', use_batch_mode=True)
with engine.connect() as conn, conn.begin():
query = """
SELECT id, name, surname, email,
COUNT(*) OVER() AS total
FROM guest
WHERE (deleted IS NULL)
ORDER BY id ASC
LIMIT 50
OFFSET 0;"""
cur = conn.execute(query)
result = cur.fetchall()