On Jan 10, 12:44 pm, Michael Bayer <[email protected]> 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 <[email protected]> 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 [email protected].
> > To unsubscribe from this group, send email to
> > [email protected].
> > 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 [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en.