Sunday, 15 September 2013

python - can an an ORM column trigger a session flush in SQLAlchemy? -



python - can an an ORM column trigger a session flush in SQLAlchemy? -

question

can property access trigger session flush in sqlalchemy? expectation for, e.g., queries attached object via column_property() or @hybrid_property cause session autoflush, in same way queries made through session.query() do. not seem case.

in simple illustration below, business relationship contains entry collection. provides "balance" property, constructed column_property(), exposes select-sum query. new entries appear in account's balance if session.flush() called explicitly.

this behavior seems suboptimal: users of business relationship class need sprinkle flush() calls throughout code based on knowing internals of balance implementation. if implementation changes---e.g., if "balance" python @property---bugs can introduced though business relationship interface identical. there alternative?

complete example class="lang-python prettyprint-override">import sys import sqlalchemy sa import sqlalchemy.sql import sqlalchemy.orm import sqlalchemy.ext.declarative base of operations = sa.ext.declarative.declarative_base() class entry(base): __tablename__ = "entries" id = sa.column(sa.integer, primary_key=true) value = sa.column(sa.numeric, primary_key=true) account_id = sa.column(sa.integer, sa.foreignkey("accounts.id")) business relationship = sa.orm.relationship("account", backref="entries") class account(base): __tablename__ = "accounts" id = sa.column(sa.integer, primary_key=true) balance = sa.orm.column_property( sa.sql.select([sa.sql.func.sum(entry.value)]) .where(entry.account_id == id) ) def example(database_url): # connect database , prepare schema engine = sa.create_engine(database_url) session = sa.orm.sessionmaker(bind=engine)() base.metadata.create_all(bind = engine) # add together entry business relationship business relationship = account() account.entries.append(entry(value = 42)) session.add(account) # , entry in balance print "account.balance:", account.balance assert account.balance == 42 if __name__ == "__main__": example(sys.argv[1]) observed output class="lang-sh prettyprint-override">$ python sa_column_property_example.py postgres:///za_test account.balance: none traceback (most recent phone call last): file "sa_column_property_example.py", line 46, in <module> example(sys.argv[1]) file "sa_column_property_example.py", line 43, in illustration assert account.balance == 42 assertionerror preferred output

i'd see "account.balance: 42", without adding explicit phone call session.flush().

a column_property evaluated @ query time, when query(account), when attribute expired, if said session.expire("account", ['balance']).

to have attribute invoke query every time, utilize @property (some little mods here script work sqlite):

import sys import sqlalchemy sa import sqlalchemy.sql import sqlalchemy.orm import sqlalchemy.ext.declarative base of operations = sa.ext.declarative.declarative_base() class entry(base): __tablename__ = "entries" id = sa.column(sa.integer, primary_key=true) value = sa.column(sa.numeric) account_id = sa.column(sa.integer, sa.foreignkey("accounts.id")) business relationship = sa.orm.relationship("account", backref="entries") class account(base): __tablename__ = "accounts" id = sa.column(sa.integer, primary_key=true) @property def balance(self): homecoming sqlalchemy.orm.object_session(self).query( sa.sql.func.sum(entry.value) ).filter(entry.account_id == self.id).scalar() def example(database_url): # connect database , prepare schema engine = sa.create_engine(database_url, echo=true) session = sa.orm.sessionmaker(bind=engine)() base.metadata.create_all(bind = engine) # add together entry business relationship business relationship = account() account.entries.append(entry(value = 42)) session.add(account) # , entry in balance print "account.balance:", account.balance assert account.balance == 42 if __name__ == "__main__": example("sqlite://")

note "flushing" not have worry about; autoflush feature ensure flush called each time query() goes database results, it's ensuring query occurs we're going for.

another approach issue utilize hybrids. i'd recommend reading overview of 3 methods @ sql expressions mapped attributes lists out tradeoffs each approach.

python postgresql orm sqlalchemy

No comments:

Post a Comment