2013-06-03 5 views
2

Если есть следующие настройки ОРМА в SQLAlchemy:SQLAlchemy, жадная загрузка на объекте обновления

class Foo(Base): 
    id = Column(Integer, primary_key=True) 
    status = Column(String) 
    barId = Column(Integer, ForeignKey("bar.id")) 
    bar = relationship("Bar", lazy="joined") 

class Bar(Base): 
    id = Column(Integer, primary_key=True) 

Так что я хотел бы всегда иметь соответствующий объект Bar, доступный для каждого объекта Foo. Я часто отделяю объекты Foo от сеанса и продолжаю использовать его значения и значения Bar. Время от времени мне нужно обновить поле статуса Foo. В этом случае я создаю новый сеанс, добавляю объект foo в сеанс и фиксирую его. После фиксации объект Bar, связанный с объектом Foo, недействителен, но не перезагружается неявным обновлением объекта Foo. После отсоединения объекта Foo от сеанса объект Bar больше не может использоваться. Единственный способ, с которым я нашел работу, - это явно загружать объект bar после совершения foo.

Пример работы потока:

session = Session() 
foo = session.query(Foo).get(id) <-- foo.bar is automatically eager loaded 
session.close() 
.... 
session = Session() 
session.add(foo) 
foo.status = 'done' 
session.commit()  <-- foo is commited and refreshed, foo.bar is not 
session.refresh(foo) <-- same here, foo.bar is not loaded 
#foo.bar    <-- only explicit eager loading foo.bar here works 
session.close() 
.... 
foo.bar    <-- error if not explicitly eager loaded 

Я хотел бы использовать эту установку для нескольких из тех малых Бара, как объекты. Требование, чтобы я помнил, чтобы всегда явно перезагружать, объект foo.bar подвержен ошибкам. Поэтому мой вопрос: могу ли я загрузить загрузку foo.bar во всех ситуациях, будь то запрос(), commit() (неявное обновление) или (явное) обновление()?

ответ

2

Во-первых, «commit()» не является «обновлением» - на самом деле он заканчивает все данные, поэтому вы увидите, что все отображаемые атрибуты больше не присутствуют в foo.__dict__. Неявное обновление происходит, когда вы снова касаетесь этих атрибутов. Это очень распространенная практика: просто установить expire_on_commit=False в пределах Session для тех многих приложений, для которых не требуется синхронная синхронизация транзакций после коммита, так что это, вероятно, будет самым неявным рабочим процессом.

Следующее, session.refresh(foo) будет загружать bar с помощью настроенного загружаемого загрузчика. Не уверен, почему вы видите foo.bar не загружен, я проверил, и эта функция вернется хотя бы к версии 0.5. Простой тест подтверждает это:

from sqlalchemy import * 
from sqlalchemy.orm import * 
from sqlalchemy.ext.declarative import declarative_base 

Base = declarative_base() 

class Foo(Base): 
    __tablename__ = 'foo' 
    id = Column(Integer, primary_key=True) 
    status = Column(String) 
    barId = Column(Integer, ForeignKey("bar.id")) 
    bar = relationship("Bar", lazy="joined") 

class Bar(Base): 
    __tablename__ = 'bar' 
    id = Column(Integer, primary_key=True) 

e = create_engine("sqlite://", echo=True) 
Base.metadata.create_all(e) 

s = Session(e) 

s.add(Foo(id=1, bar=Bar())) 
s.commit() 

f1 = s.query(Foo).get(1) 
f1.status = 'done' 
s.commit() 

assert 'bar' not in f1.__dict__ 
s.refresh(f1) 
assert 'bar' in f1.__dict__ 
s.close() 

assert f1.bar.id == 1 

следующей вещи, SQLAlchemy отпугивает использование объектов в их «отключенном» состояние по общей причине, что ваш отображенный объект представляет собой прокси-сервер для текущей транзакции базы данных. Поэтому, когда транзакция завершается, все данные истекли. Лично я не думаю, что обычно существует веская причина, что объекты должны использоваться в отдельном состоянии; отряд в основном предназначен для транспортировки объектов на другие сеансы, хранения их в кешках, таких вещей. Но у нас есть много пользователей, которые в любом случае полагаются на отдельные шаблоны использования, и я убедился, что могу поддержать их в разумной степени, поэтому я не стал бы слишком беспокоиться об этом.

+0

В моей конкретной установке 's.commit()' делает выбор в объекте foo без присоединения к нему со строкой (прежде чем я коснусь любого attr). Я не знаю, почему. Я ошибочно принял его за неявное обновление, как вы указали. Я не могу воспроизвести это с помощью вашей маленькой демонстрации. Нежелательная загрузка работает с явным 's.refresh (foo)'. Я пропустил это так или иначе, я все еще многому научился/экспериментировал с SQLAlchemy. – dieterg

+0

SQLAlchemy не рекомендует использовать объекты в их «отключенном» состоянии. Мои объекты Foo живут по нескольким HTTP-запросам, и я всегда закрываю сеанс orm в конце запроса. Мне нужно только обновить объект foo. 1/100 запрашивает другие 99. Я просто использую данные. Какой дизайн вы тогда порекомендовали бы? 1) Скопируйте данные Foo и Bar в объекты non orm и запрашивайте, если мне нужно обновить. 2) Откройте новый сеанс orm, даже если мне не нужно обновлять/обновлять Foo. 3) ... – dieterg

+1

обычно веб-приложение загружает все данные, необходимые для каждого запроса. если вы загружаете свой объект Foo по запросу номер один, тогда запрос номер два появится через шесть часов, вы сохранили эти объекты «Foo» в памяти все эти шесть часов? Откуда вы знаете, что они все еще актуальны? Это действительно шаблон кэширования.Рекомендация SQLAlchemy для кэширования заключается в том, что объекты повторно присоединяются или объединяются в новый сеанс по каждому запросу - см. [Кэширование кистей] (http://docs.sqlalchemy.org/en/rel_0_8/orm/examples.html#dogpile-caching) для рекомендуемых рекомендаций по проектированию. – zzzeek

Смежные вопросы