понедельник, 8 августа 2011 г.

Что такое исключение NotYet и с чем его едят

Надо сказать, что исключение NotYet штука не частая, но от того и менее понятная начинающим (и не очень начинающим) разработчикам использующим в работе ZTK и ZODB.


Исключение zope.keyreference.interfaces.NotYet возникает в момент, когда программа пытается получить ссылку (reference) на объект, который ещё не добавлен в базу данных. Пример кода вызывающего исключение NotYet при работе с ZODB:
class Foo(Persistent):
    pass

foo = Foo()
# Далее будет вызвано исключение NotYet
reference = zope.keyreference.interfaces.IKeyReference(foo)
Что бы исключения не было, необходимо сначала добавить объект foo например в корень ZODB:
root = getRoot() # получаем корень ZODB
root['foo'] = foo
reference = zope.keyreference.interfaces.IKeyReference(foo)
Чаще всего эта ошибка может возникнуть во время работы утилиты IIntIds, когда программа пытается добавить объект в контейнер, который ещё не добавлен в базу. Происходит это из-за того, что адаптер персистентного объекта к интерфейсу IKeyReference, вызываемый в утилите, пытается получить из объекта коннектор к базе . Не находя его в объекте он ищет коннектор в родителе (parrent) объекта, т.е. в контейнере, который ещё не добавлен в базу и не имеет своего родителя.
Если вам что то в этом не понятно, то рекомендую взглянуть на код zope.keyreference.persistent.

Когда дело касается явной работы с объектами и контейнерами, то с нахождением причины возникновения исключения NotYet как правило проблем нет. Другим случаем является вариант когда контейнер мы получаем не напрямую, а например из аннотаций. Если для создания адаптера, который создаёт и возвращает из аннотаций контейнер, использовать функцию-фабрику zope.annotation.factory.factory(), то в качестве родителя контейнера мы получим адаптируемый объект. Это и может стать причиной ошибки, если объект не является персистентным или сам ещё не имеет родителя (не добавлен в базу).
Реальным примером возникновения исключения NotYet в этом случае может быть использование контейнера из аннотаций для принципала (principal). Чаще всего принципал - это не персистентный объект. И поскольку он становится родителем контейнера, то это и вызывает исключение NotYet при попытке добавить что либо в аннотацию в той же транзакции, что и создание этого контейнера.
Избавится от исключения можно например следующими способами:
  • отказаться от использования фабрики zope.annotation.factory.factory() и написать свой адаптер, который в качестве родителя будет указывать в контейнере например утилиту IPrincipalAnnotation;
  • создавать контейнер и добавлять в него объекты в разных транзакциях;
  • добавлять объекты в только что созданный контейнер в обработчике события  IObjectAddedEvent вызываемого в фабрике zope.annotation.factory.factory() при его добавлении в аннотации ещё до того как родитель (parent) будет изменён на принципала.

Комментариев нет:

Отправить комментарий