Hi, 
The situation is that I have tow tables (User and UserGrp) and two 
association tables (one for many-to-many between User and UserGrp, one for 
many-to-many between UserGrp and user group permissions). Each user can 
belong to multiple user groups, and each user group can have multiple 
permissions. The permissions that a user has is the union of the 
permissions of the groups that it belongs to.

I cached the permission of each user in the User table (see the *
User._perms_cache* attribute). So it is necessary to invalidate the cache 
when the relationship between users and user groups gets changed. To 
invalidate the cache, I do an UPDATE on all the affected users, and I also 
have to expire the _perms_cache attribute of all persistent User instances. 
However, I searched the docs and couldn't find an appropriate API (*
Session.expire_all* does not work, which is shown later; 
*Session.expire*requires an instance).

I looked into SQLAlchemy's source, and finally found that I can iterate 
over all the persistent objects via *Session.identity_map*. But this is not 
documented, and do I really have to do this?

The simplified model is attached (sorry...it's still that long....)

And I also wonder why the following does not work? It raises 
an IntegrityError.

python test.py 
>>> g0.perms.add(5)
>>> ses.expire_all()
>>> ses.commit()



Finally, thanks very much for your patient reading!

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To view this discussion on the web visit 
https://groups.google.com/d/msg/sqlalchemy/-/tJCdWyGW2nQJ.
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.

from sqlalchemy import Column, event
from sqlalchemy.types import Integer, Text
from sqlalchemy.schema import ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.orm.session import object_session
from sqlalchemy.ext.associationproxy import association_proxy

from sqlalchemy.ext.declarative import declarative_base 

Base = declarative_base()

class MapUserGrpAndGrpPerm(Base):
    __tablename__ = 'map_ugrp_grpperm'

    def __init__(self, perm):
        self.perm = perm

    gid = Column(Integer, ForeignKey('ugrp.id'), primary_key = True)

    perm = Column(Integer, primary_key = True)



class MapUserAndUserGrp(Base):
    __tablename__ = 'map_user_ugrp'

    uid = Column(Integer, ForeignKey('user.id'), primary_key = True)
    gid = Column(Integer, ForeignKey('ugrp.id'), primary_key = True)



class User(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key = True)

    groups = relationship('UserGrp', secondary = 'map_user_ugrp')

    _perms_cache_rst = None
    _perms_cache = Column('permscache', Text())

    @property
    def perms(self):
        if self._perms_cache_rst is None:
            self._perms_cache_rst = self._get_perms()
        return self._perms_cache_rst


    def _get_perms(self):
        if self._perms_cache is None:
            rst = set()
            for i in self.groups:
                rst.update(i.perms)
            rst = frozenset(rst)
            self._perms_cache = '|' . join([str(i) for i in rst])
            return rst
        return frozenset([int(i) for i in self._perms_cache.split('|')])


class UserGrp(Base):
    __tablename__ = 'ugrp'

    id = Column(Integer, primary_key = True)

    _perms = relationship('MapUserGrpAndGrpPerm', collection_class = set)
    perms = association_proxy('_perms', 'perm')



def invalidate_user_perm_cache(session, gid):
    for (cls, pk), obj in session.identity_map.iteritems():
        if cls is User:
            session.expire(obj, ['_perms_cache'])
            obj._perms_cache_rst = None

    sub = session.query(MapUserAndUserGrp.uid) \
            .filter(MapUserAndUserGrp.gid == gid)
    session.query(User).filter(User.id.in_(sub)) \
            .update({User._perms_cache: None}, synchronize_session = False)


def _invcache_on_grp_perm_chg(target, *args):
    ses = object_session(target)
    if ses is not None:
        invalidate_user_perm_cache(ses, target.id)


#for i in 'append', 'remove', 'set':
#    event.listen(UserGrp._perms, i, _invcache_on_grp_perm_chg)


if __name__ == '__main__':
    from sqlalchemy.orm import sessionmaker
    from sqlalchemy import create_engine

    engine = create_engine('sqlite:///:memory:', echo = True)
    Base.metadata.create_all(engine)
    Session = sessionmaker(bind = engine)

    ses = Session()
    g0 = UserGrp()
    g1 = UserGrp()
    u0 = User()

    u0.groups.append(g0)
    u0.groups.append(g1)
    g0.perms.update([1, 2])
    g1.perms.update([2, 3])

    ses.add(u0)
    ses.commit()


    import code
    code.interact(local = locals())

Reply via email to