Вопрос
Может ли доступ к ресурсу запускать сеанс в SQLAlchemy? Мое ожидание было бы, например, для запросов, связанных с объектом через column_property() или @hybrid_property, чтобы вызвать сеанс autoflush, так же, как запросы, сделанные с помощью session.Query(). Кажется, это не так.может ли столбец ORM запускать сеанс в SQLAlchemy?
В простом примере ниже учетная запись содержит коллекцию записей. Он также предоставляет свойство «balance», построенное с помощью функции column_property(), которая предоставляет запрос на выборку. Новые записи отображаются только в балансе учетной записи, если session.flush() вызывается явно.
Это поведение кажется субоптимальным: пользователям класса Account необходимо посыпать вызовы flush() во всем их коде, основываясь на знании внутренних компонентов реализации баланса. Если изменение реализации - например, если раньше был «баланс», могут быть введены ошибки Python @property, хотя интерфейс учетной записи по существу идентичен. Есть ли альтернатива?
Полного пример
import sys
import sqlalchemy as sa
import sqlalchemy.sql
import sqlalchemy.orm
import sqlalchemy.ext.declarative
Base = 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"))
account = 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 to the database and prepare the schema
engine = sa.create_engine(database_url)
session = sa.orm.sessionmaker(bind=engine)()
Base.metadata.create_all(bind = engine)
# add an entry to an account
account = Account()
account.entries.append(Entry(value = 42))
session.add(account)
# and look for that entry in the balance
print "account.balance:", account.balance
assert account.balance == 42
if __name__ == "__main__":
example(sys.argv[1])
Наблюдаемый Выход
$ python sa_column_property_example.py postgres:///za_test
account.balance: None
Traceback (most recent 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 example
assert account.balance == 42
AssertionError
Preferred Выход
Я хотел бы видеть "account.balance: 42", без добавления явного вызова сессии. промывать().
Цените ответ. Я думаю, что я просто наклоняюсь в конструкторских решениях SQLAlchemy здесь: в ретроспективе поведение, на которое я надеялся, было чем-то вроде: «column_property должен выполнять запрос, если и только если сеанс грязный, и используйте кешированное значение в противном случае» (а также хорошо играет с выражениями запроса). Похоже, мне придется строить это поведение с помощью hybrid_property, если я действительно этого хочу. Благодарю. – bcs
Ну, вы могли бы получить это, прослушивая события изменения атрибута и истекая в качестве столбца column_property. Я думаю, что очевидно, что если все свойства столбца истекли каждый раз, когда какой-либо атрибут где-нибудь был грязным, это было бы чрезмерным. column_prop не может самостоятельно решить, какие атрибуты в памяти он может зависеть (если они есть). – zzzeek