[sqlalchemy] Re: How to force a before_update() run without changes to instrumented attributes
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
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
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
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
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
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
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?
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?
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?
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
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
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
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()
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
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
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
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?
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.