SQLAlchemy ORM НЕ СУЩЕСТВУЕТ: определение псевдонима таблицы - PullRequest
0 голосов
/ 30 марта 2020

У меня есть три объекта: A, B, C, где C ссылки A с B с (A-*C-B). Я хочу найти экземпляры A, для которых нет экземпляра C, не связанного с B.

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

Следующий модульный тест иллюстрирует это:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Column, Integer, create_engine, literal

_sql_engine = create_engine('sqlite:///:memory:')
session = sessionmaker(bind=_sql_engine)()

def test_model():
    Base = declarative_base()

    class A(Base):
        __tablename__ = 'a'
        id = Column(Integer, primary_key=True)

    class B(Base):
        __tablename__ = 'b'
        id = Column(Integer, primary_key=True)

    class C(Base):
        __tablename__ = 'c'
        a = Column(Integer, primary_key=True)
        b = Column(Integer, primary_key=True)


    Base.metadata.create_all(_sql_engine)

    a = [A(id=10),      A(id=20)]
    c = [C(a=10, b=11), C(a=20, b=21)]
    b = [B(id=11)]

    session.add_all(a + c + b)
    session.commit()

    q = session.query(A).filter(
        A.id < literal(100),
        ~(
            session.query(C)
                .filter(
                A.id == C.a,
                ~(
                    session.query(B)
                    .filter(
                        B.id == C.b,
                    ).exists()
                )
            ).exists()
        )
    )

    print(q.statement)
    print(len(q.all()))

    assert len(q.all()) == 1

Тест ожидает один результат, но он получает нуль. Напечатанное утверждение SQL:

SELECT a.id 
FROM a 
WHERE a.id < :param_1 AND NOT (EXISTS (SELECT 1 
FROM c 
WHERE NOT (EXISTS (SELECT 1 
FROM b, a 
WHERE b.id = c.b AND a.id = c.a))))

Теперь мне кажется, что проблема в третьем утверждении FROM. b и a отменяют указанные выше псевдонимы и отключаются от предыдущих ограничений.

Это правильно? Это как работает scoping в SQL? Если да, я делаю ошибку с SQLAlchemy или это ошибка?

(модульный тест использует SQLite, но конечный результат должен выполняться в PostgreSQL.)

1 Ответ

0 голосов
/ 01 апреля 2020

Благодаря zzzeek на github я нашел ответ: псевдонимы затеняются в подзапросах после второго вложения. Если мы не хотим этого, мы используем коррелированные подзапросы .

Фиксированный юнит-тест:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, aliased
from sqlalchemy import Column, Integer, create_engine, literal

_sql_engine = create_engine('sqlite:///:memory:')
session = sessionmaker(bind=_sql_engine)()


def test_model():
    Base = declarative_base()

    class A(Base):
        __tablename__ = 'a'
        id = Column(Integer, primary_key=True)

    class B(Base):
        __tablename__ = 'b'
        id = Column(Integer, primary_key=True)

    class C(Base):
        __tablename__ = 'c'
        a = Column(Integer, primary_key=True)
        b = Column(Integer, primary_key=True)


    Base.metadata.create_all(_sql_engine)

    a = [A(id=10),      A(id=20)]
    c = [C(a=10, b=11), C(a=20, b=21)]
    b = [B(id=11)]

    session.add_all(a + c + b)
    session.commit()

    q = session.query(A).filter(
        A.id < literal(100),
        ~(
            session.query(C)
                .filter(
                A.id == C.a,
                ~(
                    session.query(B)
                    .filter(
                        B.id == C.b
                    ).exists().correlate(A,C)
                )
            ).exists()
        )
    )

    print(q.statement)
    print(len(q.all()))

    assert len(q.all()) == 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...