Hello everyone.

I have a kind of "virtual" class that I want to use as base class for
all the elements that are going to be stored in the database.

Initially I had:
---------- BaseClass.py ----------
class BaseClass(object):
        _id = Column("id", Integer, primary_key=True, key="id")

        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

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

And that was working really fine.

But now I want to add a relationship N:M from this BaseClass to
UserGroup, to control the visibility of the objects in the System. I
want that only certain "UserGroups" can see certain elements.

I.e.:
Let's say I have another class "Store" and I want only the users
belonging to certain userGroup(s) to be able to see some of the
"Store" instances (i.e.: only usergroups with ids 5 or 6 will be able
to see stores id== 3,4,5... only usergroups 5, 7 and 8 will be able to
access stores 5, 8) . To accomplish that, I would create an N:M
relationship between the "Store" and "UserGroup" classes:

class Store(BaseClass.BaseClass, Database.Base):
        __tablename__ = "stores"

        _name = Column("name", String(50))
        _number = Column("number", Integer)
        
        _userGroups = relationship("UserGroup", secondary=user_group_store,
                primaryjoin=lambda: Store.id == user_group_store.c.store_id,
                secondaryjoin=lambda: UserGroup.UserGroup.id ==
                                                
user_group_store.c.user_group_id,
                collection_class=set
                )

By setting this relationship, (and, of course, with the intermediate
table "user_group_store" which relates UserGroups with Stores) I can
easily "filter" the stores that a UserGroup can see using:

    query = session.query(Store.Store).join('userGroups')
                .filter(UserGroup.UserGroup.id == int(userGroupId)) .all()

But now I want to do that not only with the "Store" class but with
every element in my system, so I can filter every class stored in the
database by "UserGroup" (something like it says in
http://www.sqlalchemy.org/docs/orm/extensions/declarative.html#mixing-in-relationships,
but N:M and with a different intermediate table per class to relate to
"UserGroup")

I tried to move that relationship to the BaseClass detailed above (in
a similar way to the id):

--------------------------- BaseClass.py --------------------

from myclasses.database import Tables
#Tables is the module where the intermediate tables are defined.
# If I want to be able to relate UserGroup with Store, in that "Tables" modules
# I will create a table (in Tables.py) like:
# intermediate_allowed_user_groups_to_stores = Table(
#       "intermediate_allowed_user_groups_to_stores",
#       Base.metadata,
#       Column("id", Integer, primary_key=True),
#       Column("element_id", Integer, ForeignKey("stores.id"), key="elementId"),
#       Column("user_group_id",
#                       Integer, ForeignKey("user_groups.id"),
#                       key="userGroupId")
#)

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

        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.

Reply via email to