On Jan 10, 12:44 pm, Michael Bayer <mike...@zzzcomputing.com> wrote: > On Jan 10, 2011, at 2:21 PM, Arturo Sevilla wrote: > > > > > > > > > > > Hello, > > > Thanks again for the quick reply! > > > I tried to isolate all the mapper columns to try to make it less > > confusing, now I know that was not a good idea. > > > orm.mapper(User, user, properties={ > > 'id': user.c.ID, > > '_first_name': user.c.FirstName, > > '_middle_name': user.c.MiddleName, > > '_last_name': user.c.LastName, > > '_salutation': user.c.Salutation, > > '_username': user.c.Username, > > '_password': user.c.Password, > > '_personal_email': user.c.PersonalEmail, > > '_bussiness_email': user.c.BussinessEmail, > > '_birth_date': user.c.BirthDate, > > '_profession': user.c.Profession, > > '_contact': orm.composite(ContactInformation, user.c.HomePage, > > user.c.Telephone, user.c.Fax, > > user.c.Nextel), > > '_home_address': orm.composite( > > Address, > > user.c.HomeAddress_Street, > > user.c.HomeAddress_City, > > user.c.HomeAddress_ZipCode > > ), > > '_billing_address': orm.composite( > > Address, > > user.c.BillingAddress_Street, > > user.c.BillingAddress_City, > > user.c.BillingAddress_ZipCode > > ), > > 'active': user.c.Active > > > }) > > > I made the Address class because in some places it makes it easy to > > encapsulate this information. I don't have any relationship() as you > > can see in the mapping, that's what I would want but within the > > composite object. There is no address table in the schema as my > > intention was to avoid a join (city in this case would have been lazy > > loaded). > > > In order to get this behavior, do I must then create an address table > > and join it through a "primaryjoin" in relationship()? > > having a separate address table would certainly be preferable, sure. > > the composite approach would be viewonly: > > @property > def home_address(self): > return Address(self.home_street, self.home_city, self.home_zip) > > in 0.7 it works pretty much in this way.
What I finally did is kind of black magic (not very proud of it) though it works for what I needed. Is kind of expanding the idea of the read-only property but making it writable by injecting what I called an "updator" which basically transforms my Address class as a proxy to the User class which is mapped withouth composite columns. The mapper remains almost the same except for the composite columns: [...] '_home_address_street': user.c.HomeAddress_Street, '_home_address_city': orm.relationship( City, uselist=False, primaryjoin=user.c.HomeAddress_City == city.c.ID ), '_home_address_zipcode': user.c.HomeAddress_ZipCode, [...] The same for my billing address "property" And in my User class: class User(object): /* Many other attributes here */ def _make_updators(self): self._updators = {} def create_updator(obj, field): def _updator(value): setattr(obj, field, value) return _updator self._updators['home'] = { 'street': create_updator(self, '_home_address_street'), 'city': create_updator(self, '_home_address_city'), 'zipcode': create_updator(self, '_home_address_zipcode') } self._updators['billing'] = { 'street': create_updator(self, '_billing_address_street'), 'city': create_updator(self, '_billing_address_city'), 'zipcode': create_updator(self, '_biling_address_zipcode') } def _run_updators(self, address, type_): if address is None: street = city = zipcode = None else: street = address.street city = address.city zipcode = address.zipcode self._updators[type_]['street'](street) self._updators[type_]['city'](city) self._updators[type_]['zipcode'](zipcode) def _check_for_updators(self): if not hasattr(self, '_updators'): self._make_updators() def _check_for_property(self, name): if not hasattr(self, name): setattr(self, name, None) @property def home_address(self): # We must check for instances created by queries instead of # usual __init__ self._check_for_updators() self._check_for_property('_home_address') if self._home_address is None: self._home_address = Address( self._home_address_street, self._home_address_city, self._home_address_zipcode, ) self._home_address._updator = self._updators['home'] return self._home_address @home_address.setter def home_address(self, value): self._check_for_updators() self._check_for_property('_home_address') backup = self._home_address if value is self._billing_address: # shallow clone value = copy.copy(value) self._home_address = self._get_attribute('home_address', value, type=Address) # if it gets here then no errors were raised if backup is not None: backup._updator = None self._home_address._updator = self._updators['home'] self._run_updators(self._home_address, 'home') self._get_attribute just validates the input. The _updators array works as a register for "updator" methods which run when home_address is set or when an attribute or property in Address is changed. The Address class in this case has obviously street, city, and zipcode properties: class Address(object): /* __init__ and other stuff */ @property def street(self): return self._street @street.setter def street(self, value): self._street = self._get_attribute('street', value, max_length=300) if self._updator is not None: self._updator['street'](self._street) By making self._updator optional an Address object is not necessarily bound to a User object. > > > Thanks! > > > On Jan 10, 10:57 am, Michael Bayer <mike...@zzzcomputing.com> wrote: > >> On Jan 10, 2011, at 1:10 PM, Arturo Sevilla wrote: > > >>> Hello, > > >>> I'm trying to do the following: > > >>> 'home': orm.composite( > >>> Address, > >>> user.c.HomeAddress_Street, > >>> # The following column is a UUID but is a foreign key to a > >>> mapped > >>> # table in SQLAlchemy, ideally would be to say > >>> relationship(City) > >>> # and specify the column > >>> user.c.HomeAddress_City, > >>> user.c.HomeAddress_ZipCode > >>> ) > > >> I don't understand what this means, i think you're missing some context > >> here that would make this easier for others to understand, are there > >> *other* columns on "user" that also reference "city" ? I don't > >> understand what "relationship() and specify the column" means. > >> relationship() accepts a "primaryjoin" expression for this purpose, its in > >> the docs. If you have "HomeAddress" and "WorkAddress", you'd make two > >> relationships. > > >> I also have an intuition, since it seems like you want a variant on > >> relationship(), that composite is definitely not what you want to be > >> using, it pretty much never is, but a picture of all the relevant columns > >> here would be helpful. You definitely cannot use a column that's part of > >> a relationship() in a composite() as well and manipulating foreign keys > >> through composites is not the intended usage. > > >>> As such I tried a simple if isinstance(city, uuid.UUID): city = > >>> City.load(city), where this method is just: > >>> @classmethod > >>> def load(cls, id): > >>> session = Session() > >>> obj = session.query(cls).get(id) > >>> session.close() > >>> return obj > > >>> However during session.commit() it appears that my Session class (made > >>> with scoped_session(sessionmaker()) ) goes to None (and crashes). > >>> Is this an expected behavior? > > >> no, the scoped_session object always returns either an existing or a new > >> Session and is never None. > > > -- > > 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.