On 04/26/2016 08:09 AM, Rob Fowler wrote:
I have a complete example here:

https://gist.github.com/mianos/42cf15928f27cc9dfde9996d2e593e78


Ideas? I am sure it's possible. At the moment I am just using a "orderby
desc" on the relationship and using [0] to get the first.


this example helps a lot for me to just fill in how to make it work. The relationship system isn't sophisticated enough to locate the "User.id" column buried within a correlated subquery in order to manipulate it for lazyloads or eager loads. So we have to organize the primaryjoin so that "User.id" is not inside a subquery. Here's a way to do that:

class User(Base):
    __tablename__ = 'tbl_users'

    id = Column(types.Integer, primary_key=True)

    mobile = relationship(
        "Mobile",
        primaryjoin=(
            "and_("
            "User.id == Mobile.user_id,"

"Mobile.id.in_(select([func.max(Mobile.id)]).group_by(Mobile.user_id))"
            ")"
        ),
        uselist=False
    )


class Mobile(Base):
    __tablename__ = 'tbl_mobiles'

    id = Column(types.Integer, primary_key=True)

user_id = Column(types.Integer, ForeignKey(User.id, ondelete="CASCADE"), nullable=False)


the other way to go here is to use "relationship to non primary mapper" as detailed at http://docs.sqlalchemy.org/en/rel_1_0/orm/join_conditions.html#relationship-to-non-primary-mapper, which in fact will get you a more optimized query for this, but is more cumbersome to use on the Python side:

stmt = select([func.max(Mobile.id).label('id'), Mobile.user_id]).\
    group_by(Mobile.user_id).alias()

user_mobile = mapper(
    Mobile, stmt, primary_key=[stmt.c.id],
    non_primary=True
)

User.mobile = relationship(
    user_mobile,
    primaryjoin=User.id == stmt.c.user_id,
    viewonly=True,
    uselist=False
)


    for u, m in session.query(User, user_mobile) \
            .join(User.mobile) \
            .filter(User.id == 1082):
        print u.id, m.id

    for u in session.query(User).filter(User.id == 1082):
        print u.mobile.id


I've attached my example scripts for both as a reference.





--
You received this message because you are subscribed to the Google
Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to sqlalchemy+unsubscr...@googlegroups.com
<mailto:sqlalchemy+unsubscr...@googlegroups.com>.
To post to this group, send email to sqlalchemy@googlegroups.com
<mailto:sqlalchemy@googlegroups.com>.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
from sqlalchemy import create_engine, types, Column, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship, backref, remote, foreign
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import select, func, desc
from sqlalchemy.orm import mapper
from sqlalchemy.orm import aliased

connection = "sqlite://"

Base = declarative_base()


class User(Base):
    __tablename__ = 'tbl_users'

    id = Column(types.Integer, primary_key=True)

    mobile = relationship(
        "Mobile",
        primaryjoin=(
            "and_("
            "User.id == Mobile.user_id,"
            "Mobile.id.in_(select([func.max(Mobile.id)]).group_by(Mobile.user_id))"
            ")"
        ),
        uselist=False
    )


class Mobile(Base):
    __tablename__ = 'tbl_mobiles'

    id = Column(types.Integer, primary_key=True)

    user_id = Column(types.Integer, ForeignKey(User.id, ondelete="CASCADE"), nullable=False)


if __name__ == '__main__':
    engine = create_engine(connection, echo='debug')
    Base.metadata.create_all(engine)
    Session = sessionmaker(bind=engine)
    session = Session()

    session.add_all([
        User(id=1082),
        User(id=1083),
        Mobile(id=5, user_id=1082),
        Mobile(id=6, user_id=1082),
        Mobile(id=7, user_id=1082),
        Mobile(id=8, user_id=1083),
        Mobile(id=9, user_id=1083),
    ])

    for u, m in session.query(User, Mobile) \
            .join(User.mobile) \
            .filter(User.id == 1082):
        print u.id, m.id

    for u in session.query(User).filter(User.id == 1082):
        print u.mobile.id

    m1 = session.query(Mobile).get(7)
    assert m1 is u.mobile
from sqlalchemy import create_engine, types, Column, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship, backref, remote, foreign
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import select, func, desc
from sqlalchemy.orm import mapper
from sqlalchemy.orm import aliased

connection = "sqlite://"

Base = declarative_base()


class User(Base):
    __tablename__ = 'tbl_users'

    id = Column(types.Integer, primary_key=True)


class Mobile(Base):
    __tablename__ = 'tbl_mobiles'

    id = Column(types.Integer, primary_key=True)

    user_id = Column(types.Integer, ForeignKey(User.id, ondelete="CASCADE"), nullable=False)


stmt = select([func.max(Mobile.id).label('id'), Mobile.user_id]).\
    group_by(Mobile.user_id).alias()

user_mobile = mapper(
    Mobile, stmt, primary_key=[stmt.c.id],
    non_primary=True
)

User.mobile = relationship(
    user_mobile,
    primaryjoin=User.id == stmt.c.user_id,
    viewonly=True,
    uselist=False
)

if __name__ == '__main__':
    engine = create_engine(connection, echo='debug')
    Base.metadata.create_all(engine)
    Session = sessionmaker(bind=engine)
    session = Session()

    session.add_all([
        User(id=1082),
        User(id=1083),
        Mobile(id=5, user_id=1082),
        Mobile(id=6, user_id=1082),
        Mobile(id=7, user_id=1082),
        Mobile(id=8, user_id=1083),
        Mobile(id=9, user_id=1083),
    ])

    for u, m in session.query(User, user_mobile) \
            .join(User.mobile) \
            .filter(User.id == 1082):
        print u.id, m.id

    for u in session.query(User).filter(User.id == 1082):
        print u.mobile.id

    m1 = session.query(Mobile).get(7)
    assert m1 is u.mobile

Reply via email to