Yeeey!!

It works!

I had to deal with the primaryjoins/secondaryjoins thing but it worked.

I'm attaching it, just in case it can help someone else!

2011/3/23 Hector Blanco <white.li...@gmail.com>:
> Thank you so much!
>
> I'll let you know!
>
> P.S.:
>    just create the m2m table for the relationship as needed.
>
> ... and this is another evidence that my brain is not 100%
> functional... Why didn't it occur to me? I dunno...
>
> 2011/3/23 Michael Bayer <mike...@zzzcomputing.com>:
>>
>> On Mar 23, 2011, at 5:47 PM, Hector Blanco wrote:
>>
>>> Hello everyone.
>>>
>>>
>>> class BaseClass(object):
>>>       sqlRelationships_accelerator = None
>>>       internalAttrs_accelerator = None
>>>       properties_accelerator = None
>>>
>>>       _id = Column("id", Integer, primary_key=True, key="id")
>>>
>>>       @classmethod
>>>       def intermediate_allowed_user_groups_to_this(cls):
>>>               retval = None
>>>               try:
>>>                       mapper = class_mapper(cls)
>>>               except ormExc.UnmappedClassError:
>>>                       mapper = None
>>>
>>>               if mapper and (mapper.local_table is not None):
>>>                       try:
>>>                               retval = getattr(Tables,
>>>                                       
>>> ("intermediate_allowed_user_groups_to_%s"
>>>                                       % mapper.local_table.name))
>>>                       except KeyError:
>>>                               return None
>>>               return retval
>>>
>>>       @declared_attr
>>>       def _allowedUserGroups(cls):
>>>               if cls:
>>>                       intermediateTable = 
>>> cls.intermediate_allowed_user_groups_to_this()
>>>                       if intermediateTable is not None:
>>>                               return relationship("UserGroup",
>>>                                       secondary="intermediateTable",
>>>                                       primaryjoin="%s._id == 
>>> intermediateTable.elementId" % cls.__name__,
>>>                                       secondaryjoin="UserGroup._id == 
>>> intermediateTable.userGroupId",
>>>                                       collection_class=set
>>>                               )
>>>               return None
>>
>> there's an enormous amount of complexity here for me to gather, its not 
>> runnable either, which basically means I'm going to skip it.   In particular 
>> the whole digging into mappers and finding tables seems unnecessary, just 
>> create the m2m table for the relationship as needed.
>>
>> For the general case of "everyone has a many-to-many to X", a short example 
>> is attached.  I hope to blog more about this kind of thing as an updated 
>> approach to that discussed in the old Polymorphic Associations post.
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>>
>>>       def __hash__(self):
>>>               return int(self.id)
>>>
>>>       def setId(self, id):
>>>               """Set id"""
>>>               self._id = int(id)
>>>
>>>       def getId(self):
>>>               """Get id"""
>>>               return self._id
>>>
>>>       def setAllowedUserGroups(self, allowedUserGroups):
>>>               self._allowedUserGroups = set(allowedUserGroups)
>>>
>>>       def getAllowedUserGroups(self):
>>>               return self._allowedUserGroups
>>>
>>>       @declared_attr
>>>       def allowedUserGroups(cls):
>>>               return synonym('_allowedUserGroups',
>>>                       descriptor=property(cls.getAllowedUserGroups,
>>>                                                   cls.setAllowedUserGroups))
>>> ----------------------------------------
>>>
>>> The "intermediate_allowed_user_groups_to_this" classmethod tries to
>>> grab the intermediate table from the Tables module based on the name
>>> of the table where the actual instances of the class (descending from
>>> BaseClass) are going to be stored. Going back to the Store class, the
>>> __tablename__ is "stores". The
>>> intermediate_allowed_user_groups_to_this method will try to grab a
>>> table called "intermediate_allowed_user_groups_to_stores" (because
>>> that is the intermediate table that would link UserGroups and Stores)
>>>
>>> * What I wanted to achieve:
>>> To filter by userGroup, I just wanted to need adding an intermediate
>>> table to the Tables module relating the UserGroup with the class to
>>> filter (as I explained, if I wanted to filter "Store", which is stored
>>> in the table
>>> "stores", I just need to create a table called
>>> "intermediate_allowed_user_groups_to_stores", or if I wanted to filter
>>> "Foo", stored in the "foos" table, I would just need to create
>>> "intermediate_allowed_user_groups_to_foos" and the baseclass, with its
>>> declared_attribute relationship, helped by the
>>> "intermediate_allowed_user_groups_to_this" would take care of the
>>> rest.
>>>
>>> What I got:
>>>
>>> Traceback (most recent call last):
>>>  File "/home/ae/ev-cms/server/src/server/app.py", line 30, in __init__
>>>    SetupDB.setupDB()
>>>  File "/home/ae/ev-cms/backlib/database/SetupDB.py", line 26, in setupDB
>>>    populateWithSamples()
>>>  File "/home/ae/ev-cms/backlib/database/SetupDB.py", line 86, in
>>>            populateWithSamples
>>>    samples = Store.Store.getSamples()
>>>  File "/home/ae/ev-cms/backlib/store/Store.py", line 379, in getSamples
>>>    store = cls()
>>>  File "<string>", line 4, in __init__
>>>  File 
>>> "/home/ae/.buildout/eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/state.py",
>>> line 111, in initialize_instance
>>>    return manager.events.original_init(*mixed[1:], **kwargs)
>>>  File "/home/ae/ev-cms/backlib/store/Store.py", line 73, in __init__
>>>    self.allowedUserGroups = set()
>>>  File 
>>> "/home/ae/.buildout/eggs/SQLAlchemy-0.6.6-py2.6.egg/sqlalchemy/orm/attributes.py",
>>> line 210, in __set__
>>>    return descriptor.__set__(instance, value)
>>>  File "/home/ae/ev-cms/backlib/database/BaseClass.py", line 80, in
>>> setAllowedUserGroups
>>>    self._allowedUserGroups = set(allowedUserGroups)
>>> AttributeError: can't set attribute
>>>
>>>
>>> ... it didn't even get pass the Store.Store constructor... :-D
>>>
>>> class Store(BaseClass.BaseClass, declarative_base)
>>>       def __init__(self):
>>>               super(Store, self).__init__()
>>>               self.name = ""
>>>               self.allowedUserGroups = set() # Crack!
>>>
>>> If I remove the line self.allowedUserGroups = set() from the
>>> constructor, I get this:
>>>  File "/home/ae/ev-cms/backlib/database/BaseClass.py", line 86, in
>>> addAllowedUserGroup
>>>    self.allowedUserGroups.add(userGroup)
>>> AttributeError: 'RelationshipProperty' object has no attribute 'add'
>>>
>>> I guess is not that easy... :-)
>>>
>>> I'm sure it's doable... but I don't know how... Any hint will be 
>>> appreciated.
>>>
>>> As usual, thank you in advance
>>>
>>> --
>>> You received this message because you are subscribed to the Google Groups 
>>> "sqlalchemy" group.
>>> To post to this group, send email to sqlalchemy@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 sqlalchemy@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 sqlalchemy@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.

import logging
import backlib
from backlib.database import Database
from backlib.database.utils import Utils as DatabaseUtils
from backlib.utils import Utils
from lxml import etree
import simplejson
from sqlalchemy import Column
from sqlalchemy import ForeignKey
from sqlalchemy import Table
from sqlalchemy import exc
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import relationship
from sqlalchemy.orm import synonym
from sqlalchemy.types import Integer

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

class BaseClass(object):
	sqlRelationships_accelerator = None
	internalAttrs_accelerator = None
	properties_accelerator = None

	_id = Column("id", Integer, primary_key=True, key="id")

	@declared_attr
	def _allowedUserGroups(cls):
		secondaryName = 'intermediate_user_groups_to_%s' % cls.__tablename__
		secondary = Table(secondaryName,
			cls.metadata,
			Column('element_id', Integer, ForeignKey('%s.id' % cls.__tablename__)),
			Column('user_group_id', Integer, ForeignKey('user_groups.id')),
		)

		return relationship(
			"UserGroup",
			secondary=secondary,
			primaryjoin="%s.c.id == %s.c.element_id" % (cls.__tablename__, secondaryName),
			secondaryjoin="user_groups.c.id == %s.c.user_group_id" % secondaryName,
			collection_class=set
		)

	def __init__(self):
		super(BaseClass, self).__init__()

	def _initAccelerators(self):
		if self.__class__.sqlRelationships_accelerator is None:
			self.__class__.sqlRelationships_accelerator = DatabaseUtils.getRelationships(self.__class__)
		if self.__class__.internalAttrs_accelerator is None:
			self.__class__.internalAttrs_accelerator = DatabaseUtils.getForeignKeys(self.__class__)
		if self.__class__.properties_accelerator is None:
			self.__class__.properties_accelerator = DatabaseUtils.getProperties(self.__class__)
			
	def __hash__(self):
		return int(self.id)

	def setId(self, id):
		"""Set id"""
		self._id = int(id)

	def getId(self):
		"""Get id"""
		return self._id

	def setAllowedUserGroups(self, allowedUserGroups):
		self._allowedUserGroups = set(allowedUserGroups)

	def getAllowedUserGroups(self):
		return self._allowedUserGroups

	def addAllowedUserGroup(self, userGroup):
		self.allowedUserGroups.add(userGroup)

	def addAllowedUserGroupById(self, userGroupId):
		userGroup = backlib.user.UserGroupManager.UserGroupManager.getById(int(userGroupId), relationshipsToPreLoad=[])
		if userGroup:
			self.addAllowedUserGroup(userGroup)
		else:
			raise KeyError("UserGroup with id %s not found" % userGroupId)
		
	def clone(self):
		retval = type(self)()
		session = Database.Session()
		retval.id = 0
		if not(self in session):
			session.add(self)

		self._initAccelerators()
		for attr in self.__class__.properties_accelerator:
			if not(attr in self.__class__.internalAttrs_accelerator):
				attrInSelf = getattr(self, attr)
				if isinstance(attrInSelf, list) or isinstance(attrInSelf, set):
					setattr(self, attr, type(attrInSelf)()) #Reset the value in self (make it empty)
					adderFunction = None
					if attr in self.__class__.sqlRelationships_accelerator:
						adderFunction = getattr(retval, "add" + attr[0].upper() + attr[1:len(attr)-1] + "ById")
					else:
						adderFunction = getattr(retval, "add" + attr[0].upper() + attr[1:len(attr)-1])
					for val in getattr(self, attr):
						adderFunction(val)
				else:
					if attr in self.__class__.sqlRelationships_accelerator:
						getattr(retval, "set" + attr[0].upper() + attr[1:] + "ById")(getattr(self, attr).id)
					else:
						if attr == "id":
							try:
								setattr(retval, attr, getattr(self, attr))
							except TypeError:
								setattr(retval, attr, 0)
						else:
							setattr(retval, attr, getattr(self, attr))
		try:
			session.commit()
			session.flush()
			session.expunge(self)
		except exc.InvalidRequestError:
			pass
		finally:
			session.close()

		return retval

	def toJSON(self):
		"""Serializes this class into a JSON format. Validate in www.jsonlint.com"""
		return simplejson.dumps(self.toDictionary())

	def toDictionary(self):
		"""Serializes this class to a Dictionary formatted in a format 'understood' by the Javascript (client) object"""
		retval = dict()
		self._initAccelerators()
		for attr in self.__class__.properties_accelerator:
			if not(attr in self.__class__.internalAttrs_accelerator):
				value = getattr(self, attr)
				if value or value == 0:
					if (isinstance(value, list) or  isinstance(value, set)):
						if attr in self.__class__.sqlRelationships_accelerator:
							retval[attr] = list(item.id for item in value)
						else:
							retval[attr] = list(value)
					else:
						if attr in self.__class__.sqlRelationships_accelerator:
							retval[attr] = value.id
						else:
							retval[attr] = value
		return retval

	def fromJSON(self, strJSON):
		"""Gets an string formatted in JSON and fills (loads) the fields in self """
		self.fromDictionary(simplejson.loads(strJSON))

	def fromDictionary(self, dictionary):
		"""Auxiliary method that allows to load the fields from a dict() type. (useful when loading the object from a JSON) although that should never happen """
		self._initAccelerators()
		session = Database.Session()
		if not(self in session):
			session.add(self)

		for attr in dictionary:
			if not(attr in self.__class__.internalAttrs_accelerator):
				attrInSelf = getattr(self, attr)
				if isinstance(attrInSelf, list) or isinstance(attrInSelf, set):
					setattr(self, attr, type(attrInSelf)()) #Reset the value in self (make it empty)
					adderFunction = None
					if attr in self.__class__.sqlRelationships_accelerator:
						adderFunction = getattr(self, "add" + attr[0].upper() + attr[1:len(attr)-1] + "ById")
					else:
						adderFunction = getattr(self, "add" + attr[0].upper() + attr[1:len(attr)-1])
					for val in dictionary[attr]:
						adderFunction(val)
				else:
					if attr in self.__class__.sqlRelationships_accelerator:
						getattr(self, "set" + attr[0].upper() + attr[1:] + "ById")(dictionary[attr])
					else:
						setattr(self, attr, dictionary[attr])					
		try:
			session.flush()
			session.commit()
			session.expunge(self)
		except exc.InvalidRequestError:
			pass
		finally:
			session.close()

	def toXML(self):
		"""Serializes this class to an XML formatted"""
		retval = ""
		self._initAccelerators()
		tagName_accelerator = Utils.toCamelCase(self.__class__.__name__)
		retval += ("<%s>\n" % tagName_accelerator)
		for attr in self.__class__.properties_accelerator:
			if attr not in self.__class__.internalAttrs_accelerator:
				value = getattr(self, attr)
				if value or value == 0:
					if (isinstance(value, list) or  isinstance(value, set)):
						valueItems = iter(value)
						shortenedAttr_accelerator = attr[:len(attr)-1]
						retval += ("\t<%s>\n" % attr)
						for item in valueItems:
							retval += ("\t\t<%s>" % shortenedAttr_accelerator)
							if attr in self.__class__.sqlRelationships_accelerator:
								retval += str(item.id)
							else:
								retval += str(item)
							retval += ("\t\t</%s>" % shortenedAttr_accelerator)
						retval += ("\t</%s>\n" % attr)
					else:
						if attr in self.__class__.sqlRelationships_accelerator:
							retval += ("\t<%s>%s</%s>\n" % (attr, value.id, attr))
						else:
							retval += ("\t<%s>%s</%s>\n" % (attr, value, attr))

		retval += ("</%s>\n" % tagName_accelerator)

		return retval

	def fromXML(self, strXML):
		"""Unserializes this class from an XML formatted file or string"""
		try:
			if strXML.endswith(".xml"):
				doc = etree.parse(strXML)
				root = doc.getroot()
			else:
				root = etree.XML(strXML)
		except etree.XMLSyntaxError, e:
			log.warn("::fromXML > Got exception %s" % e)
			log.debug("::fromXML > Showing traceback:\n%s" % (traceback.format_exc(limit=5)))


		if root.tag == Utils.toCamelCase(self.__class__.__name__):
			session = Database.Session()
			if not(self in session):
				session.add(self)

			self._initAccelerators()
			for child in root.iterchildren():
				if child.tag not in self.__class__.internalAttrs_accelerator:
					attrInSelf = getattr(self, child.tag)
					if isinstance(attrInSelf, list) or isinstance(attrInSelf, set):
						setattr(self, child.tag, type(attrInSelf)()) #Reset the value in self (make it empty)
						adderFunction = None
						if child.tag in self.__class__.sqlRelationships_accelerator:
							adderFunction = getattr(self, "add" + child.tag[0].upper() + child.tag[1:len(child.tag)-1] + "ById")
						else:
							adderFunction = getattr(self, "add" + child.tag[0].upper() + child.tag[1:len(child.tag)-1])

						for grandChild in child.iterchildren():
							adderFunction(grandChild.text)
					else:
						if child.tag in self.__class__.sqlRelationships_accelerator:
							getattr(self, "set" + child.tag[0].upper() + child.tag[1:] + "ById")(child.text)
						else:
							setattr(self, child.tag, child.text)
			try:
				session.expunge(self)
			except exc.InvalidRequestError:
				pass
			finally:
				session.close()
		else:
			raise TypeError("Trying to initialize an instance of '%s' with the XML for a %s" % (self.__class__.__name__, root.tag))

	@declared_attr
	def id(cls):
		return synonym('_id', descriptor=property(cls.getId, cls.setId))

	@declared_attr
	def allowedUserGroups(cls):
		return synonym('_allowedUserGroups', descriptor=property(cls.getAllowedUserGroups, cls.setAllowedUserGroups))

Reply via email to