Don't allow to automatically query database when use load_only option

Issue #3073 resolved
toan nguyen created an issue

When I query object like that:

user = session.query(User).options(load_only('id', 'email')).first()

If I use user object but I don't remember I only loaded 'email', 'id' columns and used like that:

print user.name 

It'll automatically query database to set 'name' attribute. I want an option for me to don't allow it to do automatic. Because of special reason, I can't query like that

user = session.query(User.id, User.email).first()

Comments (7)

  1. Mike Bayer repo owner

    what is the "special reason"? query(User.id, User.email) would give you exactly what you want.

    if the query is more composed than that, you can use a bundle:

    from sqlalchemy.orm import Bundle
    user = session.query(Bundle('user', User.id, User.email))
    

    or another option, expunge it:

    user = session.query(User).first()
    session.expunge(user)
    

    there's a noload() option but right now it only applies to relationship properties.

  2. Mike Bayer repo owner

    so expunge() it, or .close() the session that you loaded with. hows that ? otherwise it represents a proxy to a database row.

    I also will be working on patterns soon to integrate "Bundle" with ORM objects as far as methods and other behaviors. bundles load much faster as they aren't identity mapped.

  3. toan nguyen reporter

    I can't use expunge() or close() because I still use object in session such as set user.age = 20. Anyway, thank u so much.

  4. Mike Bayer repo owner

    there's no option built in but in 0.9 we have an "uninstrumented" column loader, which you can enable using the loader API at a finer grained level:

    from sqlalchemy import *
    from sqlalchemy.orm import *
    from sqlalchemy.ext.declarative import declarative_base
    
    Base = declarative_base()
    
    class User(Base):
        __tablename__ = 'user'
    
        id = Column(Integer, primary_key=True)
        name = Column(String)
        email = Column(String)
    
    e = create_engine("sqlite://", echo=True)
    Base.metadata.create_all(e)
    sess = Session(e)
    
    sess.add(User(name='n1', email='e1'))
    sess.commit()
    
    opt1 = Load(User).set_column_strategy(
                        ['id', 'email'], {'deferred': False, 'instrument': True}
                    ).set_column_strategy(
                        '*', {'deferred': False, 'instrument': False}
                    )
    
    u1 = sess.query(User).options(opt1).first()
    print u1.name
    

    this won't render SQL but will return "None" for .name, it won't raise. There's even more stable API that can be used to inject a "raise on access" handler in there if that's interesting.

  5. Log in to comment