Skip to content
Browse files

Fixed bug in Models - find() returned duplicate models because of a m…

…emory sharing bug in the Models dictionary. Resolved now, with some changes to make sure that model attributes created by class methods don't break the build. TODO: This is hackie, may wish to fix.
  • Loading branch information...
1 parent cdd98c6 commit 53f135c6416814a1ce656fd8d33c7e6663625d2e @LBiNationalTrust LBiNationalTrust committed Sep 15, 2012
View
3 MANIFEST
@@ -22,12 +22,15 @@ Mojo/ObjectMapper/Fields.py
Mojo/ObjectMapper/ModelPrototype.py
Mojo/ObjectMapper/__init__.py
Mojo/RequestHandlers/MojoHandler.py
+Mojo/RequestHandlers/PrettyOutputMixin.py
Mojo/RequestHandlers/__init__.py
Mojo/Resources/__init__.py
+Mojo/Resources/MojoTemplates/exception.html
Mojo/Resources/Project/__init__.py
Mojo/Resources/Project/runserver.py
Mojo/Resources/Project/settings.py
Mojo/Resources/Project/Apps/__init__.py
+Mojo/Resources/Project/Apps/models.py
Mojo/Resources/Project/Apps/socket_handlers.py
Mojo/Resources/Project/Apps/ui_modules.py
Mojo/Resources/Project/Apps/urls.py
View
6 Mojo/Backends/AsyncmongoBackend/asyncmongo_backend.py
@@ -1,7 +1,7 @@
from Mojo.Backends.base_interface import CollectionModelInterface, SessionInterface
import asyncmongo
from tornado import gen
-import logging
+import logging, copy
class Session(SessionInterface):
def _setup_connection(self):
@@ -34,7 +34,9 @@ def find(self, *args, **kwargs):
return_cursor = yield gen.Task(self.session._db[self.collection_name].find, *args, **kwargs)
ret_list = return_cursor[0][0]
+
return_model_list = [self._return_model_object(i) for i in ret_list]
+
cb(return_model_list)
@gen.engine
@@ -48,7 +50,7 @@ def insert(self, documents, *args, **kwargs):
else:
clean_docs = []
for doc in documents:
- del doc.__data__['_id']
+ del doc['_id']
clean_docs.append(doc.get_value())
ids = yield gen.Task(self.session._db[self.collection_name].insert, clean_docs)
View
2 Mojo/Backends/base_interface.py
@@ -58,7 +58,7 @@ def __init__(self, session, model):
def _return_model_object(self,dict):
if dict:
- return self.model(dict)
+ return self.model.create(dict)
else:
return None
View
2 Mojo/ObjectMapper/FieldPrototype.py
@@ -101,7 +101,9 @@ def get_value(self):
Returns the value stored in the field (string representations will be shown if you print the model, override
``__str__`` and ``__unicode__`` to change this behaviour.
"""
+
if self.value:
+ self.validate()
return self.value
else:
if self.default:
View
5 Mojo/ObjectMapper/Fields.py
@@ -154,7 +154,10 @@ class ListField(Field):
def expand_list(self, list):
ret_list = []
for item in list:
- val = item.get_value()
+ if hasattr(item, 'get_value'):
+ val = item.get_value()
+ else:
+ val = item
ret_list.append(val)
return ret_list
View
83 Mojo/ObjectMapper/ModelPrototype.py
@@ -1,6 +1,9 @@
from Fields import *
from tornado import gen
from Mojo.ServerHelpers.RunServer import BACKEND_COLLECTION, DATABASE
+import copy
+
+EXCLUSIONS = ['collection_name']
class Model(dict):
"""
@@ -37,8 +40,8 @@ class MyTestModel(Model):
#Output: {'this_field':"Hello World", 'another_field':42, 'whatever_field':True}
"""
+
def __init__(self, data=None):
- super(Model, self).__init__()
self.__initialise_dictionary_from_classvars()
@@ -52,36 +55,33 @@ def get_value(self):
"""
return self.__get_value()
- def __getattr__(self, name):
- return self.__class__.__dict__[name].get_value()
+ def __getattr__(self, item):
+ print "Get attr called"
+ if self.has_key(item):
+ return self[item].get_value()
- def __setattr__(self, name, value):
- if name in self.__data__.keys():
- self.__data__[name] = value
- self.__class__.__dict__[name].value = value
+ def __setattr__(self, key, value):
+ if self.has_key(key):
+ self[key].value = value
+ self.__dict__[key].value = value
else:
- raise KeyError('No such key in model')
+ raise ValueError('Key does not exist in model')
def __len__(self):
- #Enables us to be 'falsy'
return len(self.__get_value())
- __getitem__ = __getattr__
- __setitem__ = __setattr__
-
def __initialise_dictionary_from_classvars(self):
"""
Will intitalise the model's dictionary with the various names of the
class variables
"""
+ import copy
- cls_var_list = [i for i in self.__class__.__dict__.keys() if '__' not in i and 'collection_name' not in i]
+ class_attrs = [attr for attr in self.__class__.__dict__ if '__' not in attr]
+ for c in class_attrs:
+ self.__dict__[c] = copy.deepcopy(self.__class__.__dict__[c])
+ self[c] = copy.deepcopy(self.__class__.__dict__[c])
- init_dict = dict()
- for v in cls_var_list:
- init_dict[v] = None
-
- self.__dict__['__data__'] = init_dict
def __instantiate_from_dict(self, data):
"""
@@ -90,43 +90,45 @@ def __instantiate_from_dict(self, data):
if type(data) == dict:
for key in data.keys():
- if key in self.__dict__['__data__'].keys():
+ if self.has_key(key):
val = data[key]
if isinstance(val, dict):
model_instance = self.__class__.__dict__[key].to(val)
- self.__dict__['__data__'][key] = model_instance
- self.__class__.__dict__[key].value = model_instance
+ self[key] = model_instance
+ self.__dict__[key].value = model_instance
else:
- self.__dict__['__data__'][key] = data[key]
- self.__class__.__dict__[key].value = data[key]
+ print 'ADDING %s' % data[key]
+ self[key].value = data[key]
+ self.__dict__[key].value = data[key]
+
else:
logging.warning("Ignoring '%s' from input, couldn't find matching model Field entry" % (key) )
else:
raise ValueError('Input data to model must be a dictionary')
- def __validate(self):
+ def validate(self):
"""
Validates the entire model, is called when _get_value() is called.
"""
- value_dict = self.__dict__['__data__']
- for key in value_dict.keys():
- self.__class__.__dict__[key].value = value_dict[key]
- self.__class__.__dict__[key].validate()
+
+ for key in self.keys():
+ if key not in EXCLUSIONS:
+ self[key].validate()
def __get_value(self):
"""
Return a dictionary of all the members (if it validates)
"""
- self.__validate()
+ self.validate()
- value_dict = self.__dict__['__data__']
ret_val = {}
- for key in value_dict:
- if self.__class__.__dict__[key].get_value():
- ret_val[key] = self.__class__.__dict__[key].get_value()
+ for key in self.keys():
+ if key not in EXCLUSIONS:
+ if self[key].get_value():
+ ret_val[key] = self[key].get_value()
return ret_val
@@ -156,6 +158,7 @@ def insert(klass, to_insert):
if DATABASE:
if BACKEND_COLLECTION:
this_collection = BACKEND_COLLECTION(DATABASE, klass)
+ self.validate()
ret_obj = this_collection.insert(to_insert)
return ret_obj
@@ -216,6 +219,7 @@ def save(self):
if DATABASE:
if BACKEND_COLLECTION:
this_collection = BACKEND_COLLECTION(DATABASE, self)
+ self.validate()
ret_obj = this_collection.save(self)
return ret_obj
@@ -243,6 +247,7 @@ def save_async(self, callback):
if DATABASE:
if BACKEND_COLLECTION:
this_collection = BACKEND_COLLECTION(DATABASE, self)
+ self.validate()
ret_obj = yield gen.Task(this_collection.save, self)
callback(ret_obj)
@@ -391,6 +396,18 @@ def find_one_async(klass, *args, **kwargs):
cb(ret_obj)
+ @classmethod
+ def create(cls, dictionary):
+
+ instance = cls()
+ for (key, value) in dictionary.items():
+ try:
+ setattr(instance, str(key), value)
+ except TypeError, e:
+ logging.warn(e)
+
+ return instance
+
def __repr__(self):
ret_str = "%s" % str(type(self.__get_value()))
return str(ret_str)
View
1 Mojo/RequestHandlers/PrettyOutputMixin.py
@@ -25,6 +25,7 @@
import logging
import os
import Mojo
+from tornado.web import RequestHandler
exception_template = os.path.dirname(Mojo.__file__) + '/Resources/MojoTemplates/exception.html'
View
2 Mojo/Resources/Project/Apps/models.py
@@ -0,0 +1,2 @@
+#Define your models here
+
View
26 SampleMojoProject/Apps/SampleApp/models.py
@@ -0,0 +1,26 @@
+from Mojo.ObjectMapper.ModelPrototype import Model
+from Mojo.ObjectMapper.Fields import *
+import datetime
+
+class Tag(Model):
+ _id = ObjectIDField()
+ tag_name = StringField()
+
+
+class Comment(Model):
+ _id = ObjectIDField()
+ handle = StringField()
+ comment = StringField()
+ visible = BooleanField(default=True)
+
+
+class BlogPost(Model):
+ _id = ObjectIDField()
+ title = StringField(allow_empty=False)
+ slug = StringField(allow_empty=False)
+ post_intro = StringField()
+ post_body = StringField()
+ date_published = DateTimeField(default=datetime.datetime.now())
+ tags = ListField()
+ comments = ListField()
+ published = BooleanField(default=True)
View
43 SampleMojoProject/Apps/SampleApp/testdb.py
@@ -0,0 +1,43 @@
+from models import *
+
+value_list=[1,2,3]
+v2 = []
+for item in value_list:
+ t = Tag()
+ t.tag_name = item
+ v2.append(t)
+
+
+for p in v2:
+ print 'DOT: ', p.tag_name
+ print 'DICT: ', p['tag_name']
+ p.save()
+
+
+#class mDict(dict):
+# def __init__(self):
+# class_attrs = [attr for attr in self.__class__.__dict__ if '__' not in attr]
+# for c in class_attrs:
+# self[c] = self.__class__.__dict__[c]
+#
+# def __getattr__(self, item):
+# if self.has_key(item):
+# return self[key]
+# else:
+# return None
+#
+# def __setattr__(self, key, value):
+# self[key] = value
+#
+#
+#class ndict(mDict):
+# banana = 'banana'
+#
+#c = []
+#for a in xrange(3):
+# b = ndict()
+# b.banana = a
+# c.append(b)
+#
+#print c
+
View
23 SampleMojoProject/Apps/SampleApp/views.py
@@ -1,7 +1,28 @@
from Mojo.RequestHandlers.MojoHandler import MojoRequestHandler
+from Mojo.Auth.Mixins.MojoAuthMixin import MojoAuthMixin
+from Mojo.Auth.Mixins.SessionMixins import SessionMixin_Async
+from models import *
+from tornado import gen
+import tornado.web
-class myRequestHandler(MojoRequestHandler):
+class myRequestHandler(MojoRequestHandler, MojoAuthMixin, SessionMixin_Async):
+ @gen.engine
+ @tornado.web.asynchronous
def get(self):
+ posts = yield gen.Task(BlogPost.find_async, {'published':True}, sort=[('date_published',1)])
+
+ value_list=[1,2,3]
+ v2 = []
+ for item in value_list:
+ t = Tag({'tag_name':item})
+ v2.append(t)
+
+ a = Tag({'tag_name':'banana'})
+ yield gen.Task(a.save_async)
+
+ for p in v2:
+ print p.tag_name
+
self.render('test.html', message='Your face')
View
2 SampleMojoProject/settings.py
@@ -13,7 +13,7 @@
DATABASE = {
'backend': 'Mojo.Backends.AsyncmongoBackend.asyncmongo_backend',
'is_async': False,
- 'name': 'test',
+ 'name': 'mojo_blog',
'host': '127.0.0.1',
'port': 27017
}
View
16 SampleMojoProject/static/custom_socket.js
@@ -1,8 +1,8 @@
-$(function() {
- var sClient = new io.connect('http://' + window.location.host + '/SampleAppSocket');
-
- // Establish event handlers
- sClient.on('disconnect', function() {
- sClient.socket.reconnect();
- });
-});
+//$(function() {
+// var sClient = new io.connect('http://' + window.location.host + '/SampleAppSocket');
+//
+// // Establish event handlers
+// sClient.on('disconnect', function() {
+// sClient.socket.reconnect();
+// });
+//});

0 comments on commit 53f135c

Please sign in to comment.
Something went wrong with that request. Please try again.