In SQLAlchemy, why is my load_only not filtering any columns that I have specified? In SQLAlchemy, why is my load_only not filtering any columns that I have specified? python-3.x python-3.x

In SQLAlchemy, why is my load_only not filtering any columns that I have specified?


There's no issue, just a bit of a misunderstanding;

<User(email='howard@howard.com', fullname='Howard', company='howard', address='None', password='howard')>

is the string representation of your User model object and it is the User.__repr__() method that pulls in the deferred columns as it accesses them.

Using load_only() you define a set of columns to load initially for an entity, while deferring all others. But deferring a column does not mean it's somehow unusable or contains some "no value" marker (actually it does, under the hood). When each deferred attribute is referenced for the first time SQLAlchemy will issue a SELECT in order to fetch its value. This should be apparent from logs:

In [7]: u = session.query(User).options(load_only(User.email)).first()2018-05-14 16:04:49,218 INFO sqlalchemy.engine.base.Engine SELECT user.id AS user_id, user.email AS user_email FROM user LIMIT ? OFFSET ?2018-05-14 16:04:49,218 INFO sqlalchemy.engine.base.Engine (1, 0)In [8]: u.fullname2018-05-14 16:04:53,773 INFO sqlalchemy.engine.base.Engine SELECT user.fullname AS user_fullname FROM user WHERE user.id = ?2018-05-14 16:04:53,773 INFO sqlalchemy.engine.base.Engine (2,)Out[8]: 'Bar'

You can check if a column has been deferred using the inspection API. InstanceState.unloaded holds the set of keys that have no loaded value. Using that you could modify your User.__repr__ to something like:

class User(Base):    ...    def __repr__(self):        state = inspect(self)        def ga(attr):            return (repr(getattr(self, attr))                    if attr not in state.unloaded                    else "<deferred>")        attrs = " ".join([f"{attr.key}={ga(attr.key)}"                          for attr in state.attrs])        return f"<User {attrs}>"

Alternatively you could iterate over InstanceState.attrs displaying AttributeState.loaded_value, which evaluates to the symbol NO_VALUE if a value has not been loaded:

class User(Base):    ...    def __repr__(self):        state = inspect(self)            attrs = " ".join([f"{attr.key}={attr.loaded_value!r}"                          for attr in state.attrs])        return f"<User {attrs}>"