Files
sailing-analytics/python/src/sailing.db/sailing/db/model/base.py
T
2011-06-10 16:48:01 +02:00

278 lines
8.9 KiB
Python

import datetime
import mongokit
from pymongo.objectid import ObjectId
from sailing.db.model.util import queryContent, createContent
from sailing.db import config
MARKER = object
class BaseDocument(mongokit.Document):
skip_validation = True
safe_mode = True
def _updateInternal(self, **kw):
kw['safe'] = self.safe_mode
if not hasattr(self, 'created') or \
not kw.get('created', None):
self['created'] = datetime.datetime.now()
if not hasattr(self, 'typename') or \
not kw.get('typename', None):
self['typename'] = self.__class__.__name__
return kw
def save(self, *args, **kw):
kw = self._updateInternal(**kw)
super(BaseDocument, self).save(*args, **kw)
def insert(self, *args, **kw):
kw = self._updateInternal(**kw)
super(BaseDocument, self).insert(*args, **kw)
def update(self, *args, **kw):
kw = self._updateInternal(**kw)
super(BaseDocument, self).update(*args, **kw)
class ModelBase(object):
@classmethod
def collectionName(cls):
return cls.linked_model._collection
@classmethod
def query(cls, uid, wrap=True):
uid = str(uid).strip()
klass = queryContent(cls.linked_model.__name__)
backend_instance = klass.get_from_id(ObjectId(uid))
if wrap is True:
if backend_instance:
if isinstance(backend_instance, list):
backend_instance = backend_instance[0]
return cls(backend_instance)
else:
return backend_instance
return None
@classmethod
def queryRaw(cls, input, limit=0, sortby='created', sortorder=config.DESCENDING):
klass = queryContent(cls.linked_model.__name__)
backend_instance = klass.find(input).limit(limit).sort(sortby, sortorder)
if backend_instance:
if backend_instance.count():
return [cls(ba) for ba in backend_instance]
return []
@classmethod
def queryBy(cls, limit=0, sortby='created', sortorder=config.DESCENDING, **fields):
assert isinstance(limit, int)
klass = queryContent(cls.linked_model.__name__)
# update fields with typename
if not fields.get('typename', None):
fields['typename'] = cls.linked_model.__name__
# better would be to use fetch here because this includes structure but
# because we have one type per collection this doesn't matter
backend_instance = klass.find(fields).limit(limit).sort(sortby, direction=sortorder)
if backend_instance:
if backend_instance.count():
return [cls(ba) for ba in backend_instance]
return []
@classmethod
def queryByCount(cls, limit=0, sortby='created', sortorder=config.DESCENDING, **fields):
assert isinstance(limit, int)
# update fields with typename
if not fields.get('typename', None):
fields['typename'] = cls.linked_model.__name__
klass = queryContent(cls.linked_model.__name__)
backend_instance = klass.find(fields).limit(limit).sort(sortby, direction=sortorder)
return backend_instance.count()
@classmethod
def queryCount(cls, limit=0, sortby='created', sortorder=config.DESCENDING, **fields):
# update fields with typename
if not fields.get('typename', None):
fields['typename'] = cls.linked_model.__name__
klass = queryContent(cls.linked_model.__name__)
backend_instance = klass.find(fields).limit(limit).sort(sortby, direction=sortorder)
return backend_instance.count()
@classmethod
def queryRawCount(cls, input, limit=0, sortby='created', sortorder=config.DESCENDING):
klass = queryContent(cls.linked_model.__name__)
backend_instance = klass.find(input).limit(limit).sort(sortby, direction=sortorder)
return backend_instance.count()
@classmethod
def queryOneBy(cls, **fields):
# update fields with typename
if not fields.get('typename', None):
fields['typename'] = cls.linked_model.__name__
klass = queryContent(cls.linked_model.__name__)
backend_instance = klass.find_one(fields)
if backend_instance:
return cls(backend_instance)
return None
@classmethod
def removeAllBy(cls, **fields):
if not fields.get('typename', None):
fields['typename'] = cls.linked_model.__name__
fields['atomic'] = True
klass = queryContent(cls.linked_model.__name__)
klass.collection.remove(**fields)
# assuming that we clean all objects
klass.collection.drop_indexes()
def __getstate__(self):
# we need to return a state where there is no connection
# to mongodb directly established - we just save the internal
# UID so to be able to rebuild the state later on
dc = self.__dict__.copy()
inst = dc['_instance']
if getattr(inst, '__dict__', None):
# we preserve the instance to avoid reloading it
# on unpickle - just preserving raw values
idc = {}; footprint = inst.structure
for k in footprint.keys():
# model changes can yield non-complete instances
if inst.has_key(k):
idc[str(k)] = inst[k]
idc['_id'] = dc['_instance']['_id']
del dc['_instance']
dc['_instance'] = idc
return dc
def __setstate__(self, state):
self.__dict__ = state
def __init__(self, instance = None, *args, **kw):
self._instance = instance
def __getattr__(self, key):
# instance attributes are held by backend
value = self.get(key, default=object)
# make sure to fall back to this class
if value is object:
return getattr(self.__class__, key)
return value
def get(self, key, default=None):
if key in self.__class__.linked_model.structure or key in ['_id', ]:
inst = self.__dict__['_instance']
if inst.has_key(key):
return inst.get(key)
return default
@property
def uid(self):
uid = getattr(self, '_id', None)
if uid:
return unicode(str(uid))
return None
def _backend(self):
return self.__class__.linked_model
def _instance_(self):
# use this method only for direct operations (crud)
inst = self.__dict__['_instance']
if inst:
# unpickling returns dict and not real mongokit instance
if not hasattr(inst, '__dict__'):
self.__dict__['_instance'] = self.query(inst['_id'], wrap=False)
return self.__dict__['_instance']
return inst
def getURL(self):
raise Exception, 'NotImplementedHere'
def getTitle(self):
raise Exception, 'NotImplementedHere'
def creationTime(self, pretty=False):
if pretty is True:
return pretty_date(self.created)
return self.created
def reload(self):
self._instance = self.query(self.uid, wrap=False)
def delete(self):
uid = self.uid
if not uid:
return False
self._instance_().delete()
return True
def update(self, dictionary=None, dosave=True):
klassname = self._backend().__name__
# make sure to make a difference between new and update
# new objects should always have empty uid field
if self.uid is None:
self._instance = createContent(klassname)
iis = self._instance_()
if hasattr(self, '_before_update'):
self._before_update(dictionary, creation=True)
# check that defaults are NOT set on backend
if hasattr(iis.__class__, 'default_values'):
if len(iis.__class__.default_values.keys())>0:
raise Exception, 'Implementation error: instance can not have defaults! Set defaults in model! Model: %s' % self._instance.__class__
# make sure to activate defaults
if hasattr(self.__class__, 'default_values'):
for dk,dv in self.__class__.default_values.items():
if dictionary is None \
or not dk in dictionary:
if callable(dv):
dv = dv()
iis[dk] = dv
else:
if self._instance_() is None:
self._instance = self.query(self.uid, wrap=False)
iis = self._instance_()
if hasattr(self, '_before_update'):
self._before_update(dictionary, creation=False)
if dictionary:
for k,v in dictionary.items():
iis[k] = v # need explicit set here
# save also works for update process
if dosave is True:
iis.save()