if we're talking about just the timestamp, then that would be a column property 
and if you don't want it to load normally it would be under a deferred().

An analogue of the subquery example using count() is here: 
http://docs.sqlalchemy.org/en/rel_0_8/orm/mapper_config.html#using-column-property

again, you can adapt this to look like your max() + association table:


class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)

    address_created = column_property(
        select([func.max(Address.created)]).\
                where(Address.id == UserAddresses.address_id).\
            where(UserAddresses.user_id == id).\
            correlate_except(Address, UserAddresses)
    )

On Oct 21, 2013, at 3:12 AM, Sebastian Elsner <sebast...@risefx.com> wrote:

> Thank you for the help! Additionally, I was wondering if it would be
> able to make "newest_address" an attribute on the user class, which can
> then be used in a query with ".options(joinedload('newest_address'))".
> My goal would be that I get an attribute that returns the newest'
> address date time on normal access and is also able to be eagerly
> loaded/joined in a query object.  I read up in the docs and either
> column_property or Correlated Subquery Relatonship Hybrid seems to be
> made for this. Which one should I use?
> 
> 
> 
> ButAm 20.10.2013 04:41, schrieb Michael Bayer:
>> On Oct 19, 2013, at 4:24 PM, Sebastian Elsner <sebast...@risefx.com> wrote:
>> 
>>> Hello,
>>> 
>>> using the Address and User example, where the Address is connected to
>>> the User via a many-to-many relationship, I want to get all users with
>>> the date of their newest address. This is what I have now:
>>> 
>>> s.query(User, s.query(func.max(Address.created)).\
>>> filter(Address.users.any()).correlate(User).as_scalar()).\
>>>                   outerjoin(User.addresses).all()
>>> 
>>> But this is giving me all users with the newest address in the whole
>>> address table. I think the error is in the subquery's filter, but I fail
>>> to see how I can fix it. I am also not tied to this query, so if you
>>> know a better way to get a list of all Users and their newest address
>>> date, shoot!
>> the format for this is the "select user rows + an aggregate of a related 
>> table", this format is illustrated here: 
>> http://docs.sqlalchemy.org/en/rel_0_8/orm/tutorial.html#using-subqueries  
>> where we illustrate the count of address rows per user. 
>> 
>> I see here though you have an association table in between them so that just 
>> has to be added to the subquery to create a row that goes across Address and 
>> UserAddresses, same idea though, use subquery with aggregate + group_by, 
>> (outer) join to that:
>> 
>> subq = session.query(
>>                func.max(Address.created).label("created"),
>>                UserAddresses.user_id).join(UserAddresses).\
>>                group_by(UserAddresses.user_id).subquery()
>> 
>> q = session.query(User, subq.c.created).outerjoin(subq)
>> print q.all()
>> 
>> 
>> 
>> 
>>> Here is a working example. As you can see if you run it, even Users with
>>> no Addresses assigned will get the newest address date in the query.
>>> 
>>> import datetime
>>> from sqlalchemy.ext.declarative import declarative_base
>>> from sqlalchemy.engine import create_engine
>>> from sqlalchemy.orm.session import sessionmaker
>>> from sqlalchemy.schema import Column, ForeignKey
>>> from sqlalchemy.types import Integer, DateTime, String
>>> from sqlalchemy.orm import relationship
>>> from sqlalchemy.sql.expression import func
>>> 
>>> Base = declarative_base()
>>> 
>>> 
>>> class Address(Base):
>>>   __tablename__ = 'address'
>>>   id = Column(Integer, primary_key=True)
>>>   created = Column(DateTime)
>>>   users = relationship('User', back_populates='addresses',
>>> secondary='useraddress')
>>> 
>>>   def __repr__(self):
>>>       return "Address: %s, %s" % (self.id, self.created)
>>> 
>>> 
>>> class User(Base):
>>>   __tablename__ = 'user'
>>>   id = Column(Integer, primary_key=True)
>>>   name = Column(String)
>>>   addresses = relationship('Address', back_populates='users',
>>> secondary='useraddress')
>>> 
>>>   def __repr__(self):
>>>       return "User: " + self.name
>>> 
>>> 
>>> class UserAddresses(Base):
>>>   __tablename__ = 'useraddress'
>>>   user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
>>>   address_id = Column(Integer, ForeignKey('address.id'), primary_key=True)
>>> 
>>> engine = create_engine('sqlite://')
>>> Base.metadata.create_all(engine)
>>> session = sessionmaker(engine)()
>>> 
>>> u1 = User(name="Foo")
>>> u2 = User(name="Bar")
>>> u1.addresses.append(Address(created=datetime.datetime.now()))
>>> u1.addresses.append(Address(created=datetime.datetime.now() -
>>> datetime.timedelta(days=1)))
>>> session.add(u1)
>>> session.add(u2)
>>> session.commit()
>>> print u1, u1.addresses
>>> print u2, u2.addresses
>>> print session.query(User, print session.query(User,
>>> session.query(func.max(Address.created)).filter(Address.users.any()).correlate(User).as_scalar()).outerjoin(User.addresses).all()
>>> 
>>> Cheers
>>> 
>>> Sebastian
>>> 
>>> -- 
>>> 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 http://groups.google.com/group/sqlalchemy.
>>> For more options, visit https://groups.google.com/groups/opt_out.
> 
> -- 
> 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 http://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/groups/opt_out.

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

Reply via email to