class Raba(object): "All raba object inherit from this class" __metaclass__ = _RabaSingleton_MetaClass raba_id = RabaFields.Primitive() json = RabaFields.Primitive() _raba_abstract = True def __init__(self, *a, **b): pass def unreference(self): "explicit deletes the object from the singleton reference dictionary. This is mandatory to be able to delete the object using del(). Also, any attempt to reload an object with the same parameters will result un a new instance being created" try: del (self.__class__._instances[makeRabaObjectSingletonKey( self.__class__.__name__, self._raba_namespace, self.raba_id)]) except KeyError: pass def _initDbLine(self, dbLine): self.raba_id = dbLine[self.__class__.columns['raba_id']] self.json = dbLine[self.__class__.columns['json']] lists = [] for kk, i in self.columns.iteritems(): k = self.columnsToLowerCase[kk.lower()] elmt = getattr(self._rabaClass, k) if RabaFields.isPrimitiveField(elmt): try: self.__setattr__(k, cPickle.loads(str(dbLine[i]))) except: self.__setattr__(k, dbLine[i]) elif RabaFields.isRabaObjectField(elmt): if dbLine[i] != None: val = json.loads(dbLine[i]) objClass = RabaConnection(val["raba_namespace"]).getClass( val["className"]) self.__setattr__(k, RabaPupa(objClass, val["raba_id"])) elif RabaFields.isRabaListField(elmt): if dbLine[i] == None: lists.append((k, 0)) else: lists.append((k, int(dbLine[i]))) else: raise ValueError( "Unable to set field %s to %s in Raba object %s" % (k, dbLine[i], self._rabaClass.__name__)) #~ self.rabaLists = [] for k, leng in lists: rlp = RabaListPupa(anchorObj=self, relationName=k, length=leng) self.__setattr__(k, rlp) self.rabaLists.append(rlp) def _raba__init__(self, **fieldsSet): self.sqlSave = {} self.sqlSaveQMarks = {} self.listsToSave = {} self.rabaLists = [] if self.__class__ is Raba: raise TypeError( 'Raba class should never be instanciated, use inheritance') self._runtimeId = ( self.__class__.__name__, random.random() ) #this is used only during runtime ex, to avoid circular calls self._rabaClass = self.__class__ self.connection = RabaConnection(self._rabaClass._raba_namespace) self.rabaConfiguration = RabaConfiguration( self._rabaClass._raba_namespace) self._saved = False #True if present in the database if 'initDbLine' in fieldsSet and 'initDbLine' != None: self._initDbLine(fieldsSet['initDbLine']) self._saved = True if self.raba_id == None: self.raba_id = self.connection.getNextRabaId(self) def pupa(self): """returns a pupa version of self""" return RabaPupa(self.__class__, self.raba_id) def develop(self): "Dummy fct, so when you call develop on a full developed object you don't get nasty exceptions" pass @classmethod def _parseIndex(cls, fields): con = RabaConnection(cls._raba_namespace) ff = [] rlf = [] tmpf = [] if type(fields) is types.StringType: tmpf.append(fields) else: tmpf = fields for field in tmpf: if RabaFields.isRabaListField(getattr(cls, field)): lname = con.makeRabaListTableName(cls.__name__, field) rlf.append(lname, ) else: ff.append(field) return rlf, ff @classmethod def ensureIndex(cls, fields, where='', whereValues=[]): """Add an index for field, indexes take place and slow down saves and deletes but they speed up a lot everything else. If you are going to do a lot of saves/deletes drop the indexes first re-add them afterwards Fields can be a list of fields for Multi-Column Indices or simply the name of a single field. But as RabaList are basicaly in separate tables you cannot create a multicolumn indice on them. A single index will be create for the RabaList alone""" con = RabaConnection(cls._raba_namespace) rlf, ff = cls._parseIndex(fields) ww = [] for i in range(len(whereValues)): if isRabaObject(whereValues[i]): ww.append(whereValues[i].getJsonEncoding()) for name in rlf: con.createIndex(name, 'anchor_raba_id') if len(ff) > 0: con.createIndex(cls.__name__, ff, where=where, whereValues=ww) con.commit() @classmethod def dropIndex(cls, fields): "removes an index created with ensureIndex " con = RabaConnection(cls._raba_namespace) rlf, ff = cls._parseIndex(fields) for name in rlf: con.dropIndex(name, 'anchor_raba_id') con.dropIndex(cls.__name__, ff) con.commit() @classmethod def getIndexes(cls): "returns a list of the indexes of a class" con = RabaConnection(cls._raba_namespace) idxs = [] for idx in con.getIndexes(rabaOnly=True): if idx[2] == cls.__name__: idxs.append(idx) else: for k in cls.columns: if RabaFields.isRabaListField(getattr( cls, k)) and idx[2] == con.makeRabaListTableName( cls.__name__, k): idxs.append(idx) return idxs @classmethod def flushIndexes(cls): "drops all indexes for a class" con = RabaConnection(cls._raba_namespace) for idx in cls.getIndexes(): con.dropIndexByName(idx[1]) def mutated(self): 'returns True if the object has changed since the last save' return len(self.sqlSave) > 0 or len(self.listsToSave) > 0 def save(self): if self.mutated(): if not self.raba_id: raise ValueError( "Field raba_id of self has the not int value %s therefore i cannot save the object, sorry" % (self, self.raba_id)) for k, v in self.listsToSave.iteritems(): v._save() self.sqlSave[k] = len(v) if not self._saved: #this dict is only for optimisation purpose for generating the insert sql self.sqlSaveQMarks[k] = '?' self.sqlSave['json'] = self.getJsonEncoding() if not self._saved: #this dict is only for optimisation purpose for generating the insert sql self.sqlSaveQMarks['json'] = '?' if not self._saved: values = self.sqlSave.values() sql = 'INSERT INTO %s (%s) VALUES (%s)' % ( self.__class__.__name__, ', '.join(self.sqlSave.keys()), ', '.join(self.sqlSaveQMarks.values())) else: values = self.sqlSave.values() sql = 'UPDATE %s SET %s = ? WHERE raba_id = ?' % ( self.__class__.__name__, ' = ?, '.join( self.sqlSave.keys())) values.append(self.raba_id) self.connection.execute(sql, values) self.connection.commit() self._saved = True self.sqlSave = {} self.sqlSaveQMarks = {} self.listsToSave = {} def delete(self): if self._saved: for c in self.columnsToLowerCase.itervalues(): if isRabaList(getattr(self, c)): getattr(self, c).empty() self.connection.delete(table=self.__class__.__name__, where='raba_id = ?', values=(self.raba_id, )) self.connection.commit() def copy(self): v = copy.copy(self) v.raba_id = None return v def getDctDescription(self): "returns a dict describing the object" return { 'type': RabaFields.RABA_FIELD_TYPE_IS_RABA_OBJECT, 'className': self._rabaClass.__name__, 'raba_id': self.raba_id, 'raba_namespace': self._raba_namespace } def getJsonEncoding(self): "returns a json encoding of self.getDctDescription()" return json.dumps(self.getDctDescription()) def set(self, **args): "set multiple values quickly, ex : name = woopy" for k, v in args.items(): setattr(self, k, v) def __setattr__(self, k, v): "This also keeps track of wich fields have been updated." vv = v if hasattr(self.__class__, k) and RabaFields.isField( getattr(self.__class__, k)): vSQL = None if not RabaFields.isRabaListField(getattr(self.__class__, k)): classType = getattr(self.__class__, k) if not classType.check(vv): raise ValueError( "Unable to set '%s' to value '%s'. Constrain function violation" % (k, vv)) if isRabaObject(vv): vSQL = vv.getJsonEncoding() elif isPythonPrimitive(vv): vSQL = vv else: vSQL = buffer(cPickle.dumps(vv)) self.sqlSave[k] = vSQL if not self._saved: #this dict is only for optimisation purpose for generating the insert sql self.sqlSaveQMarks[k] = '?' else: if not isRabaList(vv) and not isRabaListPupa(vv): try: vv = RabaList(v) except: raise ValueError( "Unable to set '%s' to value '%s'. Value is not a valid RabaList" % (k, vv)) currList = object.__getattribute__(self, k) if not RabaFields.isRabaListField( currList) and vv is not currList and len(currList) > 0: currList.erase() self.connection.unregisterRabalist( anchor_class_name=self.__class__.__name__, anchor_raba_id=self.raba_id, relation_name=k) vv._attachToObject(self, k) self.listsToSave[ k] = vv #self.sqlSave[k] and self.sqlSaveQMarks[k] are updated in self.save() for lists object.__setattr__(self, k, vv) def __getattribute__(self, k): try: elmt = object.__getattribute__(self, k) if RabaFields.isRabaListField(elmt): #if empty elmt = RabaListPupa(anchorObj=self, relationName=k, length=-1) object.__setattr__(self, k, elmt) elif RabaFields.isField(elmt): elmt = elmt.default return elmt except AttributeError: return self.__getattr__(k) def __getattr__(self, k): raise AttributeError('%s has no attribute "%s"' % (repr(self), k)) def __getitem__(self, k): return self.__getattribute__(k) def __setitem__(self, k, v): self.__setattr__(k, v) def __repr__(self): return "<Raba obj: %s, raba_id: %s>" % (self._runtimeId, self.raba_id) @classmethod def getFields(cls): """returns a set of the available fields. In order to be able ti securely loop of the fields, "raba_id" and "json" are not included in the set""" s = set(cls.columns.keys()) s.remove('json') s.remove('raba_id') return s @classmethod def help(cls): "returns a string of lisinting available fields" return 'Available fields for %s: %s' % (cls.__name__, ', '.join( cls.getFields()))
class C(Raba): _raba_namespace = 'test' name = RabaFields.Primitive(default='C') a = RabaFields.RabaObject('A') _raba_uniques = ['name']