2013-08-28 1 views
1

Во-первых, пример кода:Изменение родственный значение объекта автоматически на основании идентификатора от фиксации в SQLAlchemy

from sqlalchemy import create_engine 
from sqlalchemy.orm import sessionmaker 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy import Column, Integer, String, ForeignKey 
from sqlalchemy.orm import relationship 
from sqlalchemy import event 


Base = declarative_base() 


class Foo(Base): 

    __tablename__ = 'foo' 

    id = Column(Integer, primary_key=True) 
    label = Column(String) 


class Bar(Base): 

    __tablename__ = 'bar' 

    id = Column(Integer, primary_key=True) 
    foo_id = Column(Integer, ForeignKey('foo.id')) 
    foo = relationship(Foo) 

    def __init__(self, foo=None): 

     self.foo = foo if foo else Foo() 
     print 'init', self.id, self, self.foo.id, self.foo 


def _adjust_label(target, value, old_value, initiator): 

    print 'adjust', target, value, old_value, initiator 
    if value and not target.foo.label: 
     target.foo.label = 'autostring %d' % value 
     print 'adjust', target.id, target, target.foo.id, target.foo, target.foo.label 

event.listen(Bar.id, 'set', _adjust_label) 

engine = create_engine('sqlite:////path/to/some.db') 
Base.metadata.create_all(engine) 
session = sessionmaker(bind=engine)() 
bar = Bar() 
print 'pre-add', bar.id, bar, bar.foo.id, bar.foo 
session.add(bar) 
print 'added', bar.id, bar, bar.foo.id, bar.foo 
session.commit() 
print 'commited', bar.id, bar, bar.foo.id, bar.foo, bar.foo.label 

Что я получаю:

pre-add None <__main__.Bar object at 0x2929f50> 
pre-add None <__main__.Foo object at 0x292e310> 
added None <__main__.Bar object at 0x2929f50> 
added None <__main__.Foo object at 0x292e310> 
adjust <__main__.Bar object at 0x2929f50> 14 None Bar.id 
adjust None <__main__.Bar object at 0x2929f50> 
adjust 14 <__main__.Foo object at 0x292e310> autostring 14 
commited 14 <__main__.Bar object at 0x2929f50> 
commited 14 <__main__.Foo object at 0x2932e10> None 

Что меня удивляет, что в committed, bar.foo является по-разному, чем до фиксации, и что, по-видимому, в результате мое успешное изменение на bar.foo.label в прослушивателе событий выбрасывается из окна. Я пытаюсь сделать это, потому что мне нужна автоматически созданная уникальная строка для bar.foo.label, и мне нужно что-то с чуть большим значением, чем чистая случайная строка. Возможно ли это сделать автоматически, с прослушивателем событий или без него, или мне нужно обрабатывать это на более высоком уровне, чем модели ORM?

ответ

1

Благодаря @javex за то, что заставило меня переосмыслить мою проблему; размышление о моем комментарии к его ответу привело к моему решению.Я переключился с события set на Bar.id на событие after_insert на Bar, а затем отправил запрос непосредственно через соединение для обновления foo.label в функции, вызванной событием.

def _adjust_label(mapper, connection, target): 

    print 'adjust', mapper, connection, target 
    if not target.foo.label: 
     connection.execute('UPDATE foo SET label="autostring %d" WHERE id=%d' % (target.id, target.foo.id)) 

event.listen(Bar, 'after_insert', _adjust_label) 

Я должен очистить его, используя надлежащие объекты вставки и конструкторы выводов, очевидно, но концепция работает.

+0

Отличное решение. Это не произошло со мной. – javex

1

Проблема с вашим подходом заключается в следующем: Bar.id является первичным ключом. Таким образом, он создается автоматически из базы данных. В свою очередь это означает, что событие set вызывается в необычном месте: после того, как изменения вступили в силу изменения (т. Е. Создание объектов как Foo, так и Bar).

Так что SQLAlchemy думает, что это делается нажатием изменений. Теперь вы продвигаетесь вперед и вносите изменения, но SQLAlchemy этого не замечает (похоже) - вместо этого это изменение присутствует в Python, но не в базе данных.

Последний шаг (фиксация) более подробно можно просмотреть, включив журналы запросов: create_engine(..., echo=True). Теперь вы увидите, что запросы выполнены. echo="debug", и вы увидите возвращение. Если вы снова запустите его, вы заметите, что база данных снова запрашивается.

Что происходит, так это то, что SQLAlchemy возвращает все значения, включая идентификатор, и (для Bar) видит: «О, объект с этим идентификатором уже присутствует в моей сессии» и он возвращает именно этот объект. Теперь я не могу точно сказать, почему это возвращает старый объект для одного случая и новый для другого случая. Это далеко в SQLAlchemy, чем я мог бы ответить, но вот интересное удовольствие:

Если вы сделаете Bar(), то личность изменится, но если вы сделаете Bar(foo=Foo()), это не будет (по крайней мере, когда я его испытаю, т). Но в обоих случаях вы меняетесь, и вы видите, что это связано не с проблемой идентификации, а с какой-либо проблемой, с которой вы вносите изменения.

Придумать решение не так-то просто. Самая большая проблема заключается в вашей зависимости от идентификатора. Я попробовал несколько решений, но ни один из них не был действительно хорош. Тем не менее, я по крайней мере, в состоянии придумать с этой идеей для запроса:

UPDATE foo SET label="autostring " || (SELECT bar.id 
    FROM bar 
    WHERE bar.foo_id = foo.id) 
WHERE foo.id=1 

Это позволит установить метку для соответствующей foo записи на стороне сервера. Вы можете сделать это запрос, который делает это для всех:

UPDATE foo SET label="autostring " || (SELECT bar.id 
    FROM bar 
    WHERE bar.foo_id = foo.id) 
WHERE foo.label IS NULL 

Вы можете дополнительно хотеть спросить SQLAlchemy list помощи на этом: Там вы можете вообще найти более квалифицированную помощь, чем здесь.

+0

Я пытался использовать именно тот факт, что 'Bar.id' является первичным ключом и, следовательно, будет установлен, когда объект сохраняется в базе данных. Если 'bar.id' устанавливается, а' bar.foo.label' пуст, я могу предположить, что в контексте моего приложения новый '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '', поэтому мне нужно заполнить его ярлык. На самом деле, возможно, я мог бы использовать вставку event hook на 'Bar' вместо этого ... –

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