sqlalchemy рассчитывать от 2 таблиц одновременно - PullRequest
1 голос
/ 16 октября 2019

У меня есть модель Case, скажем так:

class Case(Base):
    __tablename__ = "cases"
    id = db.Column("id", db.Integer, primary_key=True)

Модель пива, например:

class Beer(Base):
    __tablename__ = "beers"
    id = db.Column("id", db.Integer, primary_key=True)
    case_id = db.Column(
        "case_id", db.Integer, db.ForeignKey("cases.id"), nullable=False, index=True
    )

И модель Soda, вот так:

class Soda(Base):
    __tablename__ = "sodas"
    id = db.Column("id", db.Integer, primary_key=True)
    case_id = db.Column(
        "case_id", db.Integer, db.ForeignKey("cases.id"), nullable=False, index=True
    )

Объекты Soda или Beer обязательно принадлежат Case. Так что дело в иерархически выше, чем сода или пиво. Сода и пиво в любом случае не связаны. Сода и пиво могут принадлежать к одному и тому же делу.

Я бы хотел, чтобы в каждом случае было подсчитано количество пива и колы.

Я пробовал что-то вроде этого:

all_nbr = session.query(Case, func.count(Beer.id), func.count(Coke.id)).join(Beer, Coke).group_by(Case.id)

Но это не работает (я думаю, что это дает мне только количество пива).

У вас есть идеи?

1 Ответ

2 голосов
/ 16 октября 2019

Вы можете использовать подзапросы, чтобы решить эту проблему


import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from decimal import Decimal
from sqlalchemy import func

engine = sa.create_engine('sqlite:///:memory:')
Base = declarative_base()
session = sa.orm.sessionmaker(bind=engine)()

class Case(Base):
    __tablename__ = "cases"
    id = sa.Column("id", sa.Integer, primary_key=True)


class Beer(Base):
    __tablename__ = "beers"
    id = sa.Column("id", sa.Integer, primary_key=True)
    case_id = sa.Column(
        "case_id", sa.Integer, sa.ForeignKey("cases.id"), nullable=False, index=True
    )


class Soda(Base):
    __tablename__ = "sodas"
    id = sa.Column("id", sa.Integer, primary_key=True)
    case_id = sa.Column(
        "case_id", sa.Integer, sa.ForeignKey("cases.id"), nullable=False, index=True
    )

Base.metadata.create_all(engine)

engine.echo = True

session.add(Case(id=1))
session.add(Case(id=2))

session.add(Beer(id=1, case_id=1))
session.add(Beer(id=2, case_id=1))
session.add(Beer(id=3, case_id=1))
session.add(Beer(id=4, case_id=2))


session.add(Soda(id=1, case_id=2))
session.add(Soda(id=2, case_id=2))
session.add(Soda(id=3, case_id=2))
session.add(Soda(id=4, case_id=1))

session.commit()

beer_count = (session.query(
                Case.id, 
                func.count(Beer.id).label("total_beers")
            ).
            outerjoin(Beer, Beer.case_id == Case.id).
            group_by(Beer.case_id)
            ).subquery()

soda_count = (session.query(
                Case.id, 
                func.count(Soda.id).label("total_sodas")
            ).
            outerjoin(Soda, Soda.case_id == Case.id).
            group_by(Soda.case_id)
            ).subquery()

cases = (session.query(
            Case, 
            beer_count.c.total_beers,
            soda_count.c.total_sodas,
        ).
        outerjoin(beer_count, beer_count.c.id == Case.id)
        .outerjoin(soda_count, soda_count.c.id == Case.id)
        .group_by(Case)
        )

Это даст вам количество пива и газировки в каждом случае

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...