Как использовать order_by, определенный для отношений в SQLAlchemy и contains_eager? - PullRequest
2 голосов
/ 25 февраля 2020

Документы Zen of Joined Eager рекомендует использовать contains_eager(), если мы хотим сохранить порядок отношений, определенный в модели.

"Если мы хотели использовать только один JOIN для загрузки коллекции и упорядочения, мы используем опцию contains_eager (), описанную в разделе« Маршрутизация явных соединений / операторов в быстро загружаемые коллекции ». *

Но следующий пример, похоже, ведет себя иначе. Я должен что-то упустить, но не знаю, что.

class Parent(Base):
    __tablename__ = "parent"

    parent_id = Column(types.Integer, primary_key=True)
    name = Column(types.String(200), nullable=False)


class Child(Base):
    __tablename__ = "child"

    order = Column(types.Integer, default=0)
    name = Column(types.String(200))

    parent_id = Column(types.Integer, ForeignKey(Parent.parent_id))
    parent = relationship(
        Parent,
        backref=backref(
            "children",
            cascade="all,delete",
            order_by="Child.order",
        ),
    )
query = session.query(Parent).options(
    contains_eager(Parent.children)
).filter(Parent.parent_id == 99).filter(Child.name == "foo")

Создает следующее SQL:

SELECT parent.parent_id, parent.name,
       child.order, child.name,
FROM parent, child
WHERE parent.parent_id = 99 AND parent.name = 'foo'

По какой-то причине

ORDER BY child.order

отсутствует, даже если оно определено в relationship(). Любые подсказки?

Это прекрасно работает, если order_by указан во время запроса, но я хочу избежать многократного написания одного и того же критерия упорядочения.

1 Ответ

1 голос
/ 29 февраля 2020

Документация верна и относится к тому факту, что при использовании большинства готовых к загрузке методов загрузки запрос изменяется и может быть неоптимальным.

Тогда предлагается использовать contains_eager где:

1) пользователь отвечает за построение правильного запроса (включая объединения, фильтры, упорядочение и т. Д. c)

2) с использованием contains_eager пользователя намекает SA, что указанное отношение включает в запрос.


Способ быстрой загрузки отношения можно использовать joinedload:

q_joined = (
    session
    .query(Parent)
    .options(joinedload(Parent.children))
    .filter(Parent.parent_id == parent_id)
)

Но вы не можете применить эти дополнительные фильтры в этом случае.

Используя contains_eager, вы сделаете:

q_manual = (
    session
    .query(Parent)
    .join(Child)  # MUST HAVE THIS
    .options(contains_eager(Parent.children))
    .filter(Parent.parent_id == 99)
    # .filter(Child.name == "foo")  # you can add this, but you are "tricking" SA into believing that only these 'Child' are part of the Parent.children relationship.
    .order_by(Parent.parent_id, Child.order)  # JUST ADD THIS to solve the ordering
)
...