Skip to content

Instantly share code, notes, and snippets.

@kalda341
Created May 11, 2015 06:10
Show Gist options
  • Save kalda341/70b1184cce2d798afb4f to your computer and use it in GitHub Desktop.
Save kalda341/70b1184cce2d798afb4f to your computer and use it in GitHub Desktop.
from mongo import Mongo
"""
Metaclass to simplify use of items stored in the database. Some of this shit could
be refactored into a class that can be inherited from, but this will do for now
"""
class DatabaseLinkMeta(type):
def __new__(metaclass, classname, bases, classdict):
mongo = Mongo()
#Collection in database for class can be specified with the _collectionName
#variable, otherwise the class name with an "s" appended will be used
if classdict.get('_collection_name') == None:
classdict['_collection_name'] = classname + 's'
collecion_name = classdict['_collection_name']
#Saves finding it in the dict so many times
database_attributes = classdict['_database_attributes']
def _get_dict(obj):
return obj.db_dict
classdict['get_dict'] = _get_dict
def _objectify(klass, data):
obj = klass()
obj.db_dict = data
return obj
def _find(klass, query):
result_obj_list = []
results = mongo.find(collecion_name, query)
return (_objectify(klass, i) for i in results)
classdict['find'] = classmethod(_find)
def _find_one(klass, query):
result = mongo.find_one(collecion_name, query)
return _objectify(klass, result)
classdict['find_one'] = classmethod(_find_one)
def _save(obj):
_id = obj.db_dict.pop('_id')
if _id == None:
obj.db_dict['_id'] = mongo.insert(collecion_name, obj.db_dict)
else:
mongo.update(collecion_name, _id, obj.db_dict)
obj.db_dict['_id'] = _id
classdict['save'] = _save
def _from_database(obj):
if obj._id == None:
raise AttributeError("Cannot load an object with a None id from database")
db_obj = mongo.get(collecion_name, obj._id)
if db_obj == None:
raise ItemDoesNotExistError("No object exists with id %s" % obj._id)
obj.db_dict = db_obj
classdict['from_database'] = _from_database
#Preserve classes original constructor
class_init = classdict.get('__init__')
def _init(obj, *args, **kwargs):
obj.db_dict = {}
#Fallback ID
obj.db_dict['_id'] = None
try:
obj.db_dict['_id'] = kwargs.pop('id')
except KeyError:
pass
if not class_init == None:
#Call original constructor
class_init(obj, *args, **kwargs)
#Override constructor on class
classdict['__init__'] = _init
def _setattr(obj, attr, value):
#Do not allow changing an objects ID
if attr == '_id' and obj._id != None:
raise AttributeError("id attribute cannot be changed")
elif attr in database_attributes:
if isinstance(value, database_attributes[attr]['type']):
if database_attributes[attr].get('embedded') == False:
_id = value._id
if _id == None:
raise AttributeError("id must be set on unembedded \
object before assigning to database attribute")
obj.db_dict[attr] = _id
else:
obj.db_dict[attr] = value
else:
object.__setattr__(obj, attr, value)
classdict['__setattr__'] = _setattr
def _getattr(obj, attr):
if attr == "_id":
return obj.db_dict['_id']
elif attr in database_attributes:
if database_attributes[attr].get('embedded') == False:
#Will probably need optimisation sometime, but good enough for now
if obj.db_dict[attr] == None:
return None
attr_obj = database_attributes[attr]['type'](id=obj.db_dict[attr])
attr_obj.from_database()
return attr_obj
else:
return obj.db_dict[attr]
else:
return object.__getattribute__(obj, attr)
classdict['__getattribute__'] = _getattr
#Finally, create the class
return type.__new__(metaclass, classname, bases, classdict)
class ItemDoesNotExistError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment