Here is code:
from google.appengine.ext import db from google.appengine.ext.db import polymodel _connection_model_superclass = polymodel.PolyModel class ConnectionModelMetaclass(type(_connection_model_superclass)): def __new__(cls, name, bases, dct): myname = name.replace('ConnectionModel','').lower() if myname: #this is not the baseclass to_collection_name = 'myto_%s_connections' % myname #or any other naming scheme you like from_collection_name = 'myfrom_%s_connections' % myname #or any other naming scheme you like myto = 'myto_%s'%myname myfrom = 'myfrom_%s'%myname dct[myto] = db.ReferenceProperty(collection_name = to_collection_name) dct[myfrom] = db.ReferenceProperty(collection_name = from_collection_name) if 'put' in dct: myput = dct['put'] else: myput = None def put(self): setattr(self, myto, self.myto) setattr(self, myfrom, self.myfrom) self._validate_connected_types() if myput is not None: myput(self) else: MyClass = eval(name) super(MyClass, self).put() dct['put'] = put return super(ConnectionModelMetaclass, cls).__new__(cls, name, bases, dct) class ConnectionModel(_connection_model_superclass): __metaclass__ = ConnectionModelMetaclass ALLOWED_CONNECTIONS = {}#empty dict means anything goes. dict if of kind tuple->tuple timestamp = db.DateTimeProperty(auto_now = True) myto = db.ReferenceProperty(collection_name = 'myto_connections') myfrom = db.ReferenceProperty(collection_name = 'myfrom_connections') connection_index = db.StringProperty()#for strict sorting and paging of connections def _validate_connected_types(self): if None in (self.myfrom, self.myto): raise AttributeError if not self._check_connection(): raise AttributeError(\ 'Connection %s --> %s is not allowed for class %s', self.myfrom.__class__.__name__, self.myto.__class__.__name__, self.__class__) def _check_connection(self): if len(self.ALLOWED_CONNECTIONS) == 0: return True for froms, tos in self.ALLOWED_CONNECTIONS.iteritems(): if isinstance(self.myfrom, froms): if isinstance(self.myto, tos): return True return False def put(self): if not self.connection_index: self.connection_index = '%s|%s|%s' % \ (self.timestamp, self.myfrom.key().name(),\ self.myto.key().name()) super(ConnectionModel, self).put() class LikeConnectionModel(ConnectionModel): ALLOWED_CONNECTIONS = {UserModel : ImageModel} class FollowConnectionModel(ConnectionModel): ALLOWED_CONNECTIONS = {UserModel : (UserModel, ImageModel) }#users can follow users and (what the heck) follow images
And a break..
And more code!
#!/usr/bin/python2 """ Linux ioctl numbers made easy size can be an integer or format string compatible with struct module for example include/linux/watchdog.h: #define WATCHDOG_IOCTL_BASE 'W' struct watchdog_info { __u32 options; /* Options the card/driver supports */ __u32 firmware_version; /* Firmware version of the card */ __u8 identity[32]; /* Identity of the board */ }; #define WDIOC_GETSUPPORT _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info) becomes: WDIOC_GETSUPPORT = _IOR(ord('W'), 0, "=II32s") """ import struct # constant for linux portability _IOC_NRBITS = 8 _IOC_TYPEBITS = 8 # architecture specific _IOC_SIZEBITS = 14 _IOC_DIRBITS = 2 _IOC_NRMASK = (1 << _IOC_NRBITS) - 1 _IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1 _IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1 _IOC_DIRMASK = (1 << _IOC_DIRBITS) - 1 _IOC_NRSHIFT = 0 _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS _IOC_NONE = 0 _IOC_WRITE = 1 _IOC_READ = 2 def _IOC(dir, type, nr, size): if isinstance(size, str) or isinstance(size, unicode): size = struct.calcsize(size) return dir << _IOC_DIRSHIFT | \ type << _IOC_TYPESHIFT | \ nr << _IOC_NRSHIFT | \ size << _IOC_SIZESHIFT def _IO(type, nr): return _IOC(_IOC_NONE, type, nr, 0) def _IOR(type, nr, size): return _IOC(_IOC_READ, type, nr, size) def _IOW(type, nr, size): return _IOC(_IOC_WRITE, type, nr, size) def _IOWR(type, nr, size): return _IOC(_IOC_READ | _IOC_WRITE, type, nr, size) ## end of http://code.activestate.com/recipes/578225/ }}}