Added CollectionManager, made connection module
All connection-related functions are now in connection.py. Created a ConnectionManager class for interacting with a collection in the database. Top-level document classes have an instance of a ConnectionManager (Document.collection). Defined a 'save' method on top-level document's that uses the collection manager's '_save_document' method to save the document to the database. Added tests for CollectionManagers -- all unit tests now require a valid connection to the database, which is set up in the tests' setUp method.
This commit is contained in:
parent
688fd5af66
commit
c99f5c4ec1
@ -2,39 +2,11 @@ import document
|
|||||||
from document import *
|
from document import *
|
||||||
import fields
|
import fields
|
||||||
from fields import *
|
from fields import *
|
||||||
|
import connection
|
||||||
|
from connection import *
|
||||||
|
|
||||||
from pymongo import Connection
|
__all__ = document.__all__ + fields.__all__ + connection.__all__
|
||||||
|
|
||||||
__all__ = document.__all__ + fields.__all__ + ['connect']
|
|
||||||
|
|
||||||
__author__ = 'Harry Marr'
|
__author__ = 'Harry Marr'
|
||||||
__version__ = '0.1'
|
__version__ = '0.1'
|
||||||
|
|
||||||
_connection_settings = {
|
|
||||||
'host': 'localhost',
|
|
||||||
'port': 27017,
|
|
||||||
'pool_size': 1,
|
|
||||||
}
|
|
||||||
_connection = None
|
|
||||||
_db = None
|
|
||||||
|
|
||||||
def _get_connection():
|
|
||||||
if _connection is None:
|
|
||||||
_connection = Connection(**_connection_settings)
|
|
||||||
return _connection
|
|
||||||
|
|
||||||
def connect(db=None, username=None, password=None, **kwargs):
|
|
||||||
"""Connect to the database specified by the 'db' argument. Connection
|
|
||||||
settings may be provided here as well if the database is not running on
|
|
||||||
the default port on localhost. If authentication is needed, provide
|
|
||||||
username and password arguments as well.
|
|
||||||
"""
|
|
||||||
if db is None:
|
|
||||||
raise TypeError('"db" argument must be provided to connect()')
|
|
||||||
|
|
||||||
_connection_settings.update(kwargs)
|
|
||||||
connection = _get_connection()
|
|
||||||
# Get DB from connection and auth if necessary
|
|
||||||
_db = connection[db]
|
|
||||||
if username is not None and password is not None:
|
|
||||||
_db.authenticate(username, password)
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from collection import CollectionManager
|
||||||
|
|
||||||
|
|
||||||
class ValidationError(Exception):
|
class ValidationError(Exception):
|
||||||
@ -128,7 +129,13 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
|||||||
}
|
}
|
||||||
meta.update(attrs.get('meta', {}))
|
meta.update(attrs.get('meta', {}))
|
||||||
attrs['_meta'] = meta
|
attrs['_meta'] = meta
|
||||||
return super_new(cls, name, bases, attrs)
|
|
||||||
|
# Set up collection manager, needs the class to have fields so use
|
||||||
|
# DocumentMetaclass before instantiating CollectionManager object
|
||||||
|
new_class = super_new(cls, name, bases, attrs)
|
||||||
|
setattr(new_class, 'collection', CollectionManager(new_class))
|
||||||
|
|
||||||
|
return new_class
|
||||||
|
|
||||||
|
|
||||||
class BaseDocument(object):
|
class BaseDocument(object):
|
||||||
@ -141,7 +148,7 @@ class BaseDocument(object):
|
|||||||
setattr(self, attr_name, values.pop(attr_name))
|
setattr(self, attr_name, values.pop(attr_name))
|
||||||
else:
|
else:
|
||||||
if attr_value.required:
|
if attr_value.required:
|
||||||
raise ValidationError('Field "%s" is required' % self.name)
|
raise ValidationError('Field "%s" is required' % attr_name)
|
||||||
# Use default value
|
# Use default value
|
||||||
setattr(self, attr_name, getattr(self, attr_name, None))
|
setattr(self, attr_name, getattr(self, attr_name, None))
|
||||||
|
|
||||||
@ -179,4 +186,9 @@ class BaseDocument(object):
|
|||||||
def _to_mongo(self):
|
def _to_mongo(self):
|
||||||
"""Return data dictionary ready for use with MongoDB.
|
"""Return data dictionary ready for use with MongoDB.
|
||||||
"""
|
"""
|
||||||
return dict((k, v) for k, v in self._data.items() if v is not None)
|
data = {}
|
||||||
|
for field_name, field in self._fields.items():
|
||||||
|
value = getattr(self, field_name, None)
|
||||||
|
if value is not None:
|
||||||
|
data[field_name] = field._to_mongo(value)
|
||||||
|
return data
|
||||||
|
18
mongomap/collection.py
Normal file
18
mongomap/collection.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
from connection import _get_db
|
||||||
|
|
||||||
|
class CollectionManager(object):
|
||||||
|
|
||||||
|
def __init__(self, document):
|
||||||
|
"""Set up the collection manager for a specific document.
|
||||||
|
"""
|
||||||
|
db = _get_db()
|
||||||
|
self._document = document
|
||||||
|
self._collection_name = document._meta['collection']
|
||||||
|
# This will create the collection if it doesn't exist
|
||||||
|
self._collection = db[self._collection_name]
|
||||||
|
self._id_field = document._meta['object_id_field']
|
||||||
|
|
||||||
|
def _save_document(self, document):
|
||||||
|
"""Save the provided document to the collection.
|
||||||
|
"""
|
||||||
|
_id = self._collection.save(document)
|
48
mongomap/connection.py
Normal file
48
mongomap/connection.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
from pymongo import Connection
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['ConnectionError', 'connect']
|
||||||
|
|
||||||
|
|
||||||
|
_connection_settings = {
|
||||||
|
'host': 'localhost',
|
||||||
|
'port': 27017,
|
||||||
|
'pool_size': 1,
|
||||||
|
}
|
||||||
|
_connection = None
|
||||||
|
_db = None
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _get_connection():
|
||||||
|
global _connection
|
||||||
|
if _connection is None:
|
||||||
|
_connection = Connection(**_connection_settings)
|
||||||
|
return _connection
|
||||||
|
|
||||||
|
def _get_db():
|
||||||
|
global _db
|
||||||
|
if _db is None:
|
||||||
|
raise ConnectionError('Not connected to database')
|
||||||
|
return _db
|
||||||
|
|
||||||
|
def connect(db=None, username=None, password=None, **kwargs):
|
||||||
|
"""Connect to the database specified by the 'db' argument. Connection
|
||||||
|
settings may be provided here as well if the database is not running on
|
||||||
|
the default port on localhost. If authentication is needed, provide
|
||||||
|
username and password arguments as well.
|
||||||
|
"""
|
||||||
|
global _db
|
||||||
|
if db is None:
|
||||||
|
raise TypeError('"db" argument must be provided to connect()')
|
||||||
|
|
||||||
|
_connection_settings.update(kwargs)
|
||||||
|
connection = _get_connection()
|
||||||
|
# Get DB from connection and auth if necessary
|
||||||
|
_db = connection[db]
|
||||||
|
if username is not None and password is not None:
|
||||||
|
_db.authenticate(username, password)
|
||||||
|
|
@ -1,7 +1,5 @@
|
|||||||
from base import DocumentMetaclass, TopLevelDocumentMetaclass, BaseDocument
|
from base import DocumentMetaclass, TopLevelDocumentMetaclass, BaseDocument
|
||||||
|
|
||||||
#import pymongo
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['Document', 'EmbeddedDocument']
|
__all__ = ['Document', 'EmbeddedDocument']
|
||||||
|
|
||||||
@ -15,3 +13,5 @@ class Document(BaseDocument):
|
|||||||
|
|
||||||
__metaclass__ = TopLevelDocumentMetaclass
|
__metaclass__ = TopLevelDocumentMetaclass
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.collection._save_document(self._to_mongo())
|
||||||
|
@ -61,7 +61,13 @@ class EmbeddedDocumentField(BaseField):
|
|||||||
def _to_python(self, value):
|
def _to_python(self, value):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def _to_mongo(self, value):
|
||||||
|
return self.document._to_mongo(value)
|
||||||
|
|
||||||
def _validate(self, value):
|
def _validate(self, value):
|
||||||
|
"""Make sure that the document instance is an instance of the
|
||||||
|
EmbeddedDocument subclass provided when the document was defined.
|
||||||
|
"""
|
||||||
if not isinstance(value, self.document):
|
if not isinstance(value, self.document):
|
||||||
raise ValidationError('Invalid embedded document instance '
|
raise ValidationError('Invalid embedded document instance '
|
||||||
'provided to an EmbeddedDocumentField')
|
'provided to an EmbeddedDocumentField')
|
||||||
|
33
tests/collection.py
Normal file
33
tests/collection.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import unittest
|
||||||
|
import pymongo
|
||||||
|
|
||||||
|
from mongomap.collection import CollectionManager
|
||||||
|
from mongomap import *
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionManagerTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
connect(db='mongotest')
|
||||||
|
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
age = IntField()
|
||||||
|
self.Person = Person
|
||||||
|
|
||||||
|
def test_initialisation(self):
|
||||||
|
"""Ensure that CollectionManager is correctly initialised.
|
||||||
|
"""
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
age = IntField()
|
||||||
|
|
||||||
|
self.assertTrue(isinstance(Person.collection, CollectionManager))
|
||||||
|
self.assertEqual(Person.collection._collection_name,
|
||||||
|
Person._meta['collection'])
|
||||||
|
self.assertTrue(isinstance(Person.collection._collection,
|
||||||
|
pymongo.collection.Collection))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -1,16 +1,22 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from mongomap import *
|
from mongomap import *
|
||||||
|
from mongomap.connection import _get_db
|
||||||
|
|
||||||
|
|
||||||
class DocumentTest(unittest.TestCase):
|
class DocumentTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
connect(db='mongomaptest')
|
||||||
|
|
||||||
class Person(Document):
|
class Person(Document):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
age = IntField()
|
age = IntField()
|
||||||
self.Person = Person
|
self.Person = Person
|
||||||
|
|
||||||
|
self.db = _get_db()
|
||||||
|
self.db.drop_collection(self.Person._meta['collection'])
|
||||||
|
|
||||||
def test_definition(self):
|
def test_definition(self):
|
||||||
"""Ensure that document may be defined using fields.
|
"""Ensure that document may be defined using fields.
|
||||||
"""
|
"""
|
||||||
@ -77,6 +83,45 @@ class DocumentTest(unittest.TestCase):
|
|||||||
self.assertTrue('content' in Comment._fields)
|
self.assertTrue('content' in Comment._fields)
|
||||||
self.assertFalse(hasattr(Comment, '_meta'))
|
self.assertFalse(hasattr(Comment, '_meta'))
|
||||||
|
|
||||||
|
def test_save(self):
|
||||||
|
"""Ensure that a document may be saved in the database.
|
||||||
|
"""
|
||||||
|
# Create person object and save it to the database
|
||||||
|
person = self.Person(name='Test User', age=30)
|
||||||
|
person.save()
|
||||||
|
# Ensure that the object is in the database
|
||||||
|
collection = self.db[self.Person._meta['collection']]
|
||||||
|
person_obj = collection.find_one({'name': 'Test User'})
|
||||||
|
self.assertEqual(person_obj['name'], 'Test User')
|
||||||
|
self.assertEqual(person_obj['age'], 30)
|
||||||
|
|
||||||
|
def test_save_embedded_document(self):
|
||||||
|
"""Ensure that a document with an embedded document field may be
|
||||||
|
saved in the database.
|
||||||
|
"""
|
||||||
|
class EmployeeDetails(EmbeddedDocument):
|
||||||
|
position = StringField()
|
||||||
|
|
||||||
|
class Employee(self.Person):
|
||||||
|
salary = IntField()
|
||||||
|
details = EmbeddedDocumentField(EmployeeDetails)
|
||||||
|
|
||||||
|
# Create employee object and save it to the database
|
||||||
|
employee = Employee(name='Test Employee', age=50, salary=20000)
|
||||||
|
employee.details = EmployeeDetails(position='Developer')
|
||||||
|
employee.save()
|
||||||
|
|
||||||
|
# Ensure that the object is in the database
|
||||||
|
collection = self.db[self.Person._meta['collection']]
|
||||||
|
employee_obj = collection.find_one({'name': 'Test Employee'})
|
||||||
|
self.assertEqual(employee_obj['name'], 'Test Employee')
|
||||||
|
self.assertEqual(employee_obj['age'], 50)
|
||||||
|
# Ensure that the 'details' embedded object saved correctly
|
||||||
|
self.assertEqual(employee_obj['details']['position'], 'Developer')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.db.drop_collection(self.Person._meta['collection'])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -5,6 +5,9 @@ from mongomap import *
|
|||||||
|
|
||||||
class FieldTest(unittest.TestCase):
|
class FieldTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
connect(db='mongomaptest')
|
||||||
|
|
||||||
def test_default_values(self):
|
def test_default_values(self):
|
||||||
"""Ensure that default field values are used when creating a document.
|
"""Ensure that default field values are used when creating a document.
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user