On Jun 20, 2010, at 7:54 AM, Nikolaj wrote:

> Hi there,
> 
> I'm trying to use the UniqueObject recipe for my project which does
> not use declarative_base(). However, I'm having trouble getting this
> to work and run into this error:
> 
> sqlalchemy.orm.exc.UnmappedInstanceError: Class '__main__.Widget' is
> mapped, but this instance lacks instrumentation.  This occurs when the
> instance is created before sqlalchemy.orm.mapper(__main__.Widget) was
> called.
> 
> Is it simply not possible to use the decorator like this when the
> mapping is done module-level after the class definition? If so, is
> declarative the recommended practice now?

this particular decorator can't be used with non-declarative since it assumes 
the class has already been mapper instrumented.

however, when you aren't using declarative, that implies you would like the 
peristence of your objects to be defined separately.   So it's more appropriate 
here to decorate the mapper() function:

    mapper(Widget, widget)
    Widget = unique_constructor(Session, 
            lambda name:name, 
            lambda query, name:query.filter(Widget.name==name)
    )(Widget)


I've updated the wiki to reflect this.



> Here is my code:
> 
> # From http://www.sqlalchemy.org/trac/wiki/UsageRecipes/UniqueObject
> def unique_constructor(scoped_session, hashfunc, queryfunc):
>    def decorate(cls):
>        def _null_init(self, *arg, **kw):
>            pass
>        def __new__(cls, bases, *arg, **kw):
>            session = scoped_session()
>            cache = getattr(session, '_unique_cache', None)
>            if cache is None:
>                session._unique_cache = cache = {}
> 
>            key = (cls, hashfunc(*arg, **kw))
>            if key in cache:
>                return cache[key]
>            else:
>                q = session.query(cls)
>                q = queryfunc(q, *arg, **kw)
>                obj = q.first()
>                if not obj:
>                    obj = object.__new__(cls)
>                    obj._init(*arg, **kw)
>                    session.add(obj)
>                cache[key] = obj
>                return obj
> 
>        cls._init = cls.__init__
>        cls.__init__ = _null_init
>        cls.__new__ = classmethod(__new__)
>        return cls
> 
>    return decorate
> 
> from sqlalchemy import *
> from sqlalchemy.orm import *
> 
> engine = create_engine('sqlite://', echo=True)
> Session = scoped_session(sessionmaker(bind=engine))
> metadata = MetaData()
> 
> @unique_constructor(Session,
>    lambda name:name,
>    lambda query, name:query.filter(Widget.name==name)
> )
> class Widget(object): pass
> 
> widget_table = Table(
>    'widgets',
>    metadata,
> 
>    Column('id', Integer, nullable=False, primary_key=True,
> autoincrement=True),
>    Column('name', String(255), nullable=False)
> )
> 
> Index('idx_widgets_name', widget_table.c.name, unique=True)
> 
> mapper(Widget, widget_table)
> 
> if __name__ == '__main__':
>    metadata.create_all(engine)
> 
>    session = Session()
> 
>    w1, w2, w3 = Widget(name='w1'), Widget(name='w2'),
> Widget(name='w3')
>    w1b = Widget(name='w1')
> 
>    assert w1 is w1b
>    assert w2 is not w3
>    assert w2 is not w1
> 
>    session.commit()
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To post to this group, send email to sqlalch...@googlegroups.com.
> To unsubscribe from this group, send email to 
> sqlalchemy+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/sqlalchemy?hl=en.
> 

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to