[sqlalchemy] Re: How to force a before_update() run without changes to instrumented attributes

2010-10-27 Thread Nikolaj
Can you give an example of a forwards-compatible way to modify an
attribute to mark the instance dirty without creating actual net
changes? SA seems quite good at detecting my tricks.

On Oct 27, 6:41 pm, Michael Bayer mike...@zzzcomputing.com wrote:
 On Oct 27, 2010, at 11:43 AM, Nikolaj wrote:

  I have a mapped class called Widget, with an uninstrumented attribute
  called 'owner' (i.e. it does not have a column representation). Widget
  has a MapperExtension with a before_update() method that runs a couple
  of queries using connection.execute() and sets some attributes on the
  instance.

  I would like Widget.owner to behave like an instrumented attribute in
  the sense that modifying it forces a MapperExtension.before_update()
  run at flush-time. Is that possible?

  Alternatively, is there a way to force a before_update() run by
  marking an instance as dirty (possibly by modifying attributes without
  actual net changes)? In that case I could make Widget.owner into a
  property and mark the instance dirty in the setter.

 that last idea would work and would be forwards-compatible.   Otherwise you'd 
 need to emit a modified event to instance_state(myobject) which isn't public 
 API.



  Thanks,

  N

  --
  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 
  athttp://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.



[sqlalchemy] Cannot deepcopy() model instance with attribute mapped collection on it

2010-10-04 Thread Nikolaj
Hey,

I had trouble deepcopying model instances that use attribute mapped
collection classes. I'm aware that copying an instance implies a
certain use case with a better solution - I accidentally hit this
error when I had an instance as an attribute on an object I was
deepcopy()ing. The following example fails with TypeError: attrgetter
expected 1 arguments, got 0.

Is there a way to remedy the situation a bit so the deepcopy doesn't
fail so spectacularly?

Thanks,

N

from sqlalchemy import create_engine, Column, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker, relationship
from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.types import Integer, String

import copy

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base(bind=engine)
Session = scoped_session(sessionmaker(bind=engine))

class Note(Base):
__tablename__ = 'notes'
keyword = Column(String, primary_key=True)
item_name = Column(String, ForeignKey('items.name'))

class Item(Base):
__tablename__ = 'items'
name = Column(String, primary_key=True)

notes = relationship(Note,
collection_class=attribute_mapped_collection('keyword'))

Base.metadata.create_all()

item = Item(name='Foo')

note = Note(keyword='Bar')
item.notes.set(note)

Session.commit()

assert item.notes['Bar'] is note

copy.deepcopy(note) # Passes
copy.deepcopy(item) # Fails

-- 
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.



[sqlalchemy] Re: Uninitialized coltype in PGDialect.get_columns() for custom column basetype

2010-09-30 Thread Nikolaj
Sorry I was being a bit lazy and trying to avoid setting up the PG
test environment. The problem affects a domain over a custom type.
Here is a test that exhibits the behaviour (without assertions):

class DomainOverCustomTypeReflectionTest(TestBase,
AssertsExecutionResults):
__only_on__ = 'postgresql'

@classmethod
def setup_class(cls):
con = testing.db.connect()
con.execute(CREATE TYPE testtype AS ENUM ('test'))
con.execute('CREATE DOMAIN testdomain AS testtype')
con.execute('CREATE TABLE testtable (question integer, answer
testdomain)')

@classmethod
def teardown_class(cls):
con = testing.db.connect()
con.execute('DROP TABLE testtable')
con.execute('DROP DOMAIN testdomain')
con.execute('DROP TYPE testtype')

def test_domain_is_reflected(self):
metadata = MetaData(testing.db)
table = Table('testtable', metadata, autoload=True)

Thanks,

N

On Sep 29, 9:58 pm, Michael Bayer mike...@zzzcomputing.com wrote:
 On Sep 29, 2010, at 3:37 PM, Nikolaj wrote:



  Hi there,

  I use the earthdistance and cube modules for PostgreSQL (http://
 www.postgresql.org/docs/8.4/interactive/earthdistance.html). These
  define some custom types and functions for doing great circle
  calculations. I ran into a problem with table introspection in
  PGDialect.get_columns().

  The columns and domains fetched look like this (note that the 'earth'
  type defined by the earthdistance module has the base type of 'cube'
  from the cube module):

  rows = [
  (u'id', u'integer', unextval('mytable_id_seq'::regclass), True, 1,
  161772),
  (u'created_at', u'timestamp without time zone', None, True, 2,
  161772),
  (u'name', u'character varying(255)', None, True, 3, 161772),
  (u'lat', u'numeric(10,7)', None, False, 4, 161772),
  (u'lng', u'numeric(10,7)', None, False, 5, 161772),
  (u'earth', u'earth', None, False, 6, 161772)
  ]

  domains = {
  u'earth': {'attype': u'cube', 'default': None, 'nullable': True},
  u'information_schema.cardinal_number': {'attype': u'integer',
                                          'default': None,
                                          'nullable': True},
  u'information_schema.character_data': {'attype': u'character
  varying',
                                         'default': None,
                                         'nullable': True},
  u'information_schema.sql_identifier': {'attype': u'character
  varying',
                                         'default': None,
                                         'nullable': True},
  u'information_schema.time_stamp': {'attype': u'timestamp',
                                     'default':
  u('now'::text)::timestamp(2) with time zone,
                                     'nullable': True}
  }

  The problem is that in the loop through the rows in
  PGDialect.get_columns(), the 'earth' column's attype is in the
  dictionary of domains, but the domain attype (cube) is not in
  self.ischema_names. So coltype is never initialized, and it ends up
  having the value of the previous for loop iteration, causing it to
  fail with TypeError: 'NUMERIC' object is not callable (because the
  previous iteration ran coltype = coltype(...)).

  The expected behaviour is obviously for `coltype` to become
  sqltypes.NULLTYPE. It can be solved by initializing coltype = None
  inside the for loop.

 Hi - can you please illustrate a short test for this.  I dont work with PG 
 domains myself so its not immediately clear to me what this means exactly, is 
 it the case that this is a domain of a domain ?  if so wouldnt we want cube 
 to be in the domains list as well ?

 We have quite a few tests for reflecting types from domains in 
 test/dialects/test_postgresql.py so a test would need to be added there once 
 a solution is decided upon.



  Thanks,

  N

  --
  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 
  athttp://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.



[sqlalchemy] Uninitialized coltype in PGDialect.get_columns() for custom column basetype

2010-09-29 Thread Nikolaj
Hi there,

I use the earthdistance and cube modules for PostgreSQL (http://
www.postgresql.org/docs/8.4/interactive/earthdistance.html). These
define some custom types and functions for doing great circle
calculations. I ran into a problem with table introspection in
PGDialect.get_columns().

The columns and domains fetched look like this (note that the 'earth'
type defined by the earthdistance module has the base type of 'cube'
from the cube module):

rows = [
 (u'id', u'integer', unextval('mytable_id_seq'::regclass), True, 1,
161772),
 (u'created_at', u'timestamp without time zone', None, True, 2,
161772),
 (u'name', u'character varying(255)', None, True, 3, 161772),
 (u'lat', u'numeric(10,7)', None, False, 4, 161772),
 (u'lng', u'numeric(10,7)', None, False, 5, 161772),
 (u'earth', u'earth', None, False, 6, 161772)
]

domains = {
 u'earth': {'attype': u'cube', 'default': None, 'nullable': True},
 u'information_schema.cardinal_number': {'attype': u'integer',
 'default': None,
 'nullable': True},
 u'information_schema.character_data': {'attype': u'character
varying',
'default': None,
'nullable': True},
 u'information_schema.sql_identifier': {'attype': u'character
varying',
'default': None,
'nullable': True},
 u'information_schema.time_stamp': {'attype': u'timestamp',
'default':
u('now'::text)::timestamp(2) with time zone,
'nullable': True}
}

The problem is that in the loop through the rows in
PGDialect.get_columns(), the 'earth' column's attype is in the
dictionary of domains, but the domain attype (cube) is not in
self.ischema_names. So coltype is never initialized, and it ends up
having the value of the previous for loop iteration, causing it to
fail with TypeError: 'NUMERIC' object is not callable (because the
previous iteration ran coltype = coltype(...)).

The expected behaviour is obviously for `coltype` to become
sqltypes.NULLTYPE. It can be solved by initializing coltype = None
inside the for loop.

Thanks,

N

-- 
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.



[sqlalchemy] Declarative classproperty member problem in 0.6.4, not 0.6.3

2010-09-15 Thread Nikolaj
The following test fails in 0.6.4 but not 0.6.3 with AttributeError:
type object 'Person' has no attribute 'foo'. Is this a deliberate
change? It seems a bit weird that every @classproperty on a
declarative subclass is accessed/run on import.

from sqlalchemy import create_engine, Column, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.util import classproperty

Base = declarative_base()

class Person(Base):
__tablename__ = 'people'

name = Column(String, primary_key=True)

@classproperty
def bar(cls):
return cls.foo

-- 
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.



[sqlalchemy] Re: Merge(load=False) woes

2010-08-25 Thread Nikolaj
Ah, that makes a lot more sense now.

Your modified createfunc obviously doesn't account for queries with
tuples of entities (some of which may also not be mapped) but I get
the principle. I will just steer well clear of the memory backend -
any production cache would obviously imply pickling and its
sideeffects.

Thanks!

On Aug 25, 4:02 pm, Michael Bayer mike...@zzzcomputing.com wrote:
 On Aug 25, 2010, at 10:47 AM, Michael Bayer wrote:



  On Aug 25, 2010, at 9:37 AM, Nikolaj wrote:

  Hello,

  I'm struggling to use the Beaker caching example in my project.
  Accessing any attribute on an instance from cache triggers a load from
  database of the instance - even trying to read the primary key!

  that means the instances that you're putting in the cache are expired, or 
  at least those attributes you are attempting to read.    The beaker example 
  takes the objects from a result and sends them straight to the cache, but 
  does not detach them from the current session.  Therefore, if your backend 
  is the memory backend, the objects in the cache are the same as those in 
  your current session and they'll get expired when the session is committed 
  or rolled back.  

  There's not a really spectacular way to get around that except to not use 
  the memory backend, use one that pickles like memcached (well, pretty 
  much memcached is the only backend I'd ever use, but the file/dbm file 
  backends would work here too).

 well, this probably would work, this diff is against tip:

 diff -r 568ef214c1ac examples/beaker_caching/caching_query.py
 --- a/examples/beaker_caching/caching_query.py  Mon Aug 23 18:17:31 2010 -0400
 +++ b/examples/beaker_caching/caching_query.py  Wed Aug 25 10:55:53 2010 -0400
 @@ -63,8 +63,15 @@
             if particular attributes have been configured.

          
 +        
 +        def createfunc():
 +            items = list(Query.__iter__(self))
 +            for item in items:
 +                self.session.expunge(item)
 +            return items
 +            
          if hasattr(self, '_cache_parameters'):
 -            return self.get_value(createfunc=lambda: 
 list(Query.__iter__(self)))
 +            return self.get_value(createfunc=createfunc)
          else:
              return Query.__iter__(self)

 I've added a comment in the example regarding this.  



  The beaker_caching example runs just fine on my system and doesn't
  exhibit the same problems, although that's on SQLite and without
  Pylons. I traced through the calls to session._merge and prop.merge
  without luck, it seems to run through the column properties and merge
  as expected.

  How do I diagnose this, short of tearing my entire codebase apart? Are
  there any instance state attributes I could inspect to see where the
  merge is going wrong?

  N

  --
  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 
  athttp://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 
  athttp://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.



[sqlalchemy] Specifying additional primaryjoin conditions for bidirectional adjacency list relationship

2010-08-12 Thread Nikolaj
Hi there,

I'm exploring the bidirectional adjacency list pattern described in
the documentation (the scalar relationship is intentional):

Person.child = relationship(
Person,
uselist=False,
backref=backref('parent', remote_side=Person.id)
)

However, I'd like to add an additional primaryjoin condition,
something to the effect of:

child = aliased(Person)

Person.child_bob = relationship(
child,
uselist=False,
primaryjoin=and_(
child.parent_id == Person.id,
child.name == 'Bob'
),
backref=backref('parent', remote_side=Person.id)
)

But of course I can't use an AliasedClass in the first argument to
relationship(). Similarly I can't add the relationship property onto
an aliased parent class. What's the proper way of defining such a
relationship?

-- 
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.



[sqlalchemy] Proper way to make a TypeDecorator around Enum?

2010-08-09 Thread Nikolaj
I'm on MySQL and trying to create a type that stores enums as
VARCHAR(255). However, the following code still issues an ENUM() DDL.
What am I doing wrong here?

class StringEnum(types.TypeDecorator):
impl = types.Enum

def __init__(self, *args, **kwargs):
super(StringEnum, self).__init__(*args, native_enum=False,
**kwargs)
self.length = 255

-- 
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.



[sqlalchemy] Re: Proper way to make a TypeDecorator around Enum?

2010-08-09 Thread Nikolaj
It makes sense to subclass the type directly if I'm not messing with
result or bind values. This works:

class StringEnum(types.Enum):
def __init__(self, *args, **kwargs):
super(StringEnum, self).__init__(*args, native_enum=False,
**kwargs)
self.length = 255

I'm still curious why the TypeDecorator I pasted previously doesn't
work though. The superconstructor does the self.impl =
self.__class__.impl(...) anyway so I figured this was the better
approach than calling self.impl = Enum(...).

On Aug 9, 2:56 pm, Michael Bayer mike...@zzzcomputing.com wrote:
 TypeDecorator doesn't affect the decorated type via subclassing, it affects 
 it via the impl attribute, hence decorates.   So you say self.impl = 
 Enum(...).      You don't need to use TypeDecorator here if all you want to 
 do is create an Enum with default arguments, just subclass Enum directly.

 On Aug 9, 2010, at 8:24 AM, Nikolaj wrote:

  I'm on MySQL and trying to create a type that stores enums as
  VARCHAR(255). However, the following code still issues an ENUM() DDL.
  What am I doing wrong here?

  class StringEnum(types.TypeDecorator):
     impl = types.Enum

     def __init__(self, *args, **kwargs):
         super(StringEnum, self).__init__(*args, native_enum=False,
  **kwargs)
         self.length = 255

  --
  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 
  athttp://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.



[sqlalchemy] Re: Proper way to make a TypeDecorator around Enum?

2010-08-09 Thread Nikolaj
On Aug 9, 3:38 pm, Michael Bayer mike...@zzzcomputing.com wrote:

 its pulling off the types.Enum callable from self.__class__ (which actually 
 seems unnecessary here, self.impl should be fine), then calling that with the 
 given arguments.  So it should be setting up the native_enum=False part at 
 least.   The length attribute though would need to be set on impl.        
 There's no reason we can't make length an actual argument of Enum, though, 
 just to help with this kind of thing (I'm assuming that you're planning on 
 adding additional elements to the enum in the future and you don't want to 
 have to alter the column, correct ?).

Correct. About the self.length thing, I simply misread __getattr__ on
TypeDecorator as __setattr__. I've included my test below just for
completness' sake to show that everything works as expected.

from sqlalchemy import create_engine, Column, types
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base(bind=engine)
Session = scoped_session(sessionmaker(bind=engine))

class StringEnum(types.TypeDecorator):
impl = types.Enum

def __init__(self, *args, **kwargs):
super(StringEnum, self).__init__(*args, native_enum=False,
**kwargs)
self.impl.length = 255

class Item(Base):
__tablename__ = 'items'
name = Column(types.String, primary_key=True)
option = Column(StringEnum(('foo', 'bar')), nullable=True)

Base.metadata.create_all()

-- 
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.



[sqlalchemy] Adding columns to SELECT clause without having to filter result entities

2010-08-09 Thread Nikolaj
I have an association object pattern between Person and Item through
PersonItem. Item is subclassed using single-table inheritance into
FooItem and BarItem, with a column named 'foo' on FooItem (although
it's obviously on the same shared table as BarItem).

I'd like to query PersonItem and joinedload(Person.item) with
polymorphic columns. However, I end up resorting to the pattern on the
last line below because I can't figure out how to add columns to the
SELECT without having them appear in the result entities. Is there a
way to accomplish this more easily?

results = Session.query(PersonItem) \
.options(
joinedload(PersonItem.person, innerjoin=True),
joinedload(PersonItem.item, innerjoin=True)
) \
.add_columns(Item.__table__.c.foo) \
.all()

if results: results = zip(*results)[0]

-- 
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.



[sqlalchemy] Eagerloading a polymorphic relationship's attributes

2010-07-31 Thread Nikolaj
I have a one to one relationship between Company and Employee where
Employee is subclassed using joined table inheritance into Manager and
Engineer. I'm trying to query for a list of companies with the
employee eagerloaded including the type-specific columns.

I've been looking at the example at
http://www.sqlalchemy.org/docs/mappers.html#creating-joins-to-specific-subtypes,
but I can't figure out how to construct a query to eagerload the
attributes onto the employee on the company.

Here's what I have at the moment:

companies = Session.query(Company) \
.join((
Employee.__table__ \
.outerjoin(Engineer.__table__) \
.outerjoin(Manager.__table__),
Company.employee
)) \
.all()

which generates

SELECT company columns
FROM companies INNER JOIN (
SELECT employee columns, engineer columns, manager columns
FROM employees
LEFT OUTER JOIN engineers ON employees.id = engineers.id
LEFT OUTER JOIN managers ON employees.id = managers.id
) AS anon_1 ON anon_1.employees_id = companies.employee_id

So I guess I need to get the Engineer and Manager columns into the
field list and then for them to be populated on the Company.employee
relationship - I'm struggling with this. Is there such as thing as
with_polymorphic() for joinedload()?

My next question is about how to manage this sort of query strategy in
the long term when the total number of subtypes might get larger (e.g.
50 types of employees). The number of types per company would remain
low (e.g. a TechCompany might have Engineers and Managers, but not
Designers or Salesmen). Should I somehow store the types of employee
to query polymorphically for on a per-Company basis?

-- 
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.



[sqlalchemy] Problems with synonym property using a mixin class

2010-07-24 Thread Nikolaj
I'm having trouble inheriting a synonym property from a mixin class.
It obviously works if the call to synonym() is on the child class and
just the get/set methods are left on the mixin. Here is an example:


from sqlalchemy import create_engine, Column, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker, synonym
from sqlalchemy.types import Integer, String

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base(bind=engine)
Session = scoped_session(sessionmaker(bind=engine))

class WithAge(object):
_age =  Column('age', Integer)

def _get_age(self):
return self._age or -1

def _set_age(self, age):
self._age = age

# Doesn't work
age = synonym('_age', descriptor=property(_get_age, _set_age))

class Person(Base, WithAge):
__tablename__ = 'people'

name = Column(String, primary_key=True)

# Works
#age = synonym('_age', descriptor=property(WithAge._get_age,
WithAge._set_age))

p = Person()
print p.age # sqlalchemy.orm.properties.SynonymProperty object
at ...

-- 
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.



[sqlalchemy] Session identity map and merge()

2010-07-14 Thread Nikolaj
I'm using consecutive merge() calls to fetch the same persistent
instance from the Session.
The test case below works as I expected: the first get_item() issues a
SELECT, the second issues an UPDATE, the third does neither.

However, in my large project, using the pattern below (without the
attribute modification) seems to issue a SELECT for every call to
merge(). I'm trying to diagnose why this is happening but can't see
anything obviously wrong like committing between calls or anything.

In what circumstances are instances removed from the Session's
identity map? Is there somewhere I can read more about the internals
of this so I can gain a better understanding of the Session?

---

from sqlalchemy import create_engine, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.types import Integer, String

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base(bind=engine)
Session = scoped_session(sessionmaker(bind=engine))

class Item(Base):
__tablename__ = 'items'
id = Column(Integer, primary_key=True)
name =  Column(String(50))

Base.metadata.create_all()

i = Item(id=1, name='Foo')
Session.add(i)

Session.commit()

def get_item():
return Session.merge(Item(id=1))

one = get_item()
print one.name

one.name = 'Bar'

two = get_item()
print two.name

three = get_item()
print three.name

-- 
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.



[sqlalchemy] Mapper object has no attribute _props

2010-07-11 Thread Nikolaj
I was trying to add an AttributeExtension to a property from my
MapperExtension but I get this error when trying to access the
property:

AttributeError: 'Mapper' object has no attribute '_props'

Here's a test case:

from sqlalchemy import create_engine, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.types import Integer, String
from sqlalchemy.orm import scoped_session, sessionmaker, deferred
from sqlalchemy.orm.interfaces import MapperExtension

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base(bind=engine)

class MyExtension(MapperExtension):
def __init__(self, property):
self.property = property

def instrument_class(self, mapper, class_):
property = mapper.get_property(self.property)
print property

class Item(Base):
__tablename__ = 'items'
__mapper_args__ = {'extension': MyExtension('name')}

id = Column(Integer, primary_key=True)
name =  deferred(Column(String))

-- 
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.



[sqlalchemy] Re: Mapper object has no attribute _props

2010-07-11 Thread Nikolaj
Ah, it's pretty clear why this happens in Mapper.__init__:

self._configure_extensions()
self._configure_class_instrumentation()
self._configure_properties()

Should MapperExtensions that want to extend properties mess with
mapper._init_properties instead?

On Jul 11, 2:49 pm, Nikolaj nik@gmail.com wrote:
 I was trying to add an AttributeExtension to a property from my
 MapperExtension but I get this error when trying to access the
 property:

 AttributeError: 'Mapper' object has no attribute '_props'

 Here's a test case:

 from sqlalchemy import create_engine, Column
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy.types import Integer, String
 from sqlalchemy.orm import scoped_session, sessionmaker, deferred
 from sqlalchemy.orm.interfaces import MapperExtension

 engine = create_engine('sqlite:///:memory:', echo=True)
 Base = declarative_base(bind=engine)

 class MyExtension(MapperExtension):
     def __init__(self, property):
         self.property = property

     def instrument_class(self, mapper, class_):
         property = mapper.get_property(self.property)
         print property

 class Item(Base):
     __tablename__ = 'items'
     __mapper_args__ = {'extension': MyExtension('name')}

     id = Column(Integer, primary_key=True)
     name =  deferred(Column(String))

-- 
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.



[sqlalchemy] Composite column with a relationship

2010-07-07 Thread Nikolaj
Hi there,

I'd like to create a column composite for amounts of money and their
currency. However, the difficulty comes in keeping the currencies in a
separate table and enabling my composite to find this relationship.

Is there some way to set the Currency on my Money type implementation
automatically?

Apologies for the amount of code here:

from sqlalchemy import create_engine, Column, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker, composite,
relationship, joinedload
from sqlalchemy.types import Integer, String, Numeric, TypeDecorator

from decimal import Decimal
import warnings

warnings.simplefilter('ignore')

class MoneyComposite(object):
def __init__(self, amount, currency):
self.amount = amount
self.currency = currency

# Set the currency on the Money type here

def __composite_values__(self):
return (self.amount, self.currency)

class MoneyType(TypeDecorator):
impl = Numeric

def __init__(self, *args, **kwargs):
super(MoneyType, self).__init__(*args, precision=11, scale=2,
**kwargs)

def process_bind_param(self, value, dialect):
if isinstance(value, Money):
value = value.value
return value

def process_result_value(self, value, dialect):
return Money(value)

class Money(object):
def __init__(self, value):
self.value = value
self.currency = Currency(symbol='USD', rate=Decimal('1.5'))

def __str__(self):
return '%s %s' % (self.currency.symbol, self.value *
self.currency.rate)

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base(bind=engine)
Session = scoped_session(sessionmaker(bind=engine))

class Currency(Base):
__tablename__ = 'currencies'
symbol = Column(String, primary_key=True)
rate = Column(Numeric)

class Item(Base):
__tablename__ = 'items'
id = Column(Integer, primary_key=True)
name =  Column(String(50))

price = composite(
MoneyComposite,
Column('amount', MoneyType),
Column('currency', String, ForeignKey(Currency.symbol))
)

currency = relationship(Currency)

Base.metadata.create_all()

c = Currency(symbol='USD', rate=Decimal('1.5'))
Session.add(c)

price = MoneyComposite(Money('5'), c.symbol)

i = Item(name='foobar', price=price)
Session.add(i)

Session.commit()

i = Session.query(Item).options(joinedload(Item.currency,
innerjoin=True)).first()
print i.price.amount

-- 
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.



[sqlalchemy] UsageRecipes/UniqueObject without using declarative?

2010-06-20 Thread Nikolaj
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? 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.