Skip to content
Browse files

Merge pull request #101 from grokful/master

Issue #100
  • Loading branch information...
2 parents caab00d + c468358 commit 5f5f4b269834ec6e463b39865cde287bd02158b6 @jeffjenkins committed Dec 23, 2012
Showing with 88 additions and 14 deletions.
  1. +18 −0 mongoalchemy/fields/fields.py
  2. +1 −3 mongoalchemy/ops.py
  3. +23 −9 mongoalchemy/session.py
  4. +16 −0 test/test_fields.py
  5. +29 −1 test/test_session.py
  6. +1 −1 test/test_update_expressions.py
View
18 mongoalchemy/fields/fields.py
@@ -61,6 +61,24 @@ def validate_wrap(self, value):
if self.min is not None and len(value) < self.min:
self._fail_validation(value, 'Value too short (%d)' % len(value))
+class RegExStringField(PrimitiveField):
+ ''' Unicode Strings. ``unicode`` is used to wrap and unwrap values,
+ and any subclass of basestring is an acceptable input, as long as
+ it matches the provided regex.'''
+ def __init__(self, regex, **kwargs):
+ ''' :param regex: instance of :class: `RegexObject` to match against
+ :param kwargs: arguments for :class:`Field`
+ '''
+ self.regex = regex
+ super(RegExStringField, self).__init__(constructor=unicode, **kwargs)
+
+ def validate_wrap(self, value):
+ ''' Validates the type and length of ``value`` '''
+ if not isinstance(value, basestring):
+ self._fail_validation_type(value, basestring)
+ if self.regex.match(value) is None:
+ self._fail_validation(value, 'Value does not match regular expression')
+
class BinaryField(PrimitiveField):
def __init__(self, **kwargs):
super(BinaryField, self).__init__(constructor=Binary, **kwargs)
View
4 mongoalchemy/ops.py
@@ -14,9 +14,7 @@ def collection(self):
return self.session.db[self.type.get_collection_name()]
def ensure_indexes(self):
- c = self.collection
- for index in self.type.get_indexes():
- index.ensure(c)
+ self.session.auto_ensure_indexes(self.type)
class ClearCollectionOp(Operation):
def __init__(self, trans_id, session, kind):
View
32 mongoalchemy/session.py
@@ -52,14 +52,17 @@
class Session(object):
- def __init__(self, database, tz_aware=False, timezone=None, safe=False, cache_size=0):
+ def __init__(self, database, tz_aware=False, timezone=None, safe=False, cache_size=0,
+ auto_ensure=True):
'''
Create a session connecting to `database`.
:param database: the database to connect to. Should be an instance of \
:class:`pymongo.database.Database`
:param safe: Whether the "safe" option should be used on mongo writes, \
blocking to make sure there are no errors.
+ :param auto_ensure: Whether to implicitly call ensure_indexes on all write \
+ operations.
**Fields**:
* db: the underlying pymongo database object
@@ -76,6 +79,7 @@ def __init__(self, database, tz_aware=False, timezone=None, safe=False, cache_si
self.safe = safe
self.timezone = timezone
self.tz_aware = bool(tz_aware or timezone)
+ self.auto_ensure = auto_ensure
self.cache_size = cache_size
self.cache = {}
@@ -88,14 +92,16 @@ def in_transaction(self):
return len(self.transactions) > 0
@classmethod
- def connect(self, database, timezone=None, cache_size=0, *args, **kwds):
+ def connect(self, database, timezone=None, cache_size=0, auto_ensure=True, *args, **kwds):
''' `connect` is a thin wrapper around __init__ which creates the
database connection that the session will use.
:param database: the database name to use. Should be an instance of \
:class:`basestring`
:param safe: The value for the "safe" parameter of the Session \
init function
+ :param auto_ensure: Whether to implicitly call ensure_indexes on all write \
+ operations.
:param args: arguments for :class:`pymongo.connection.Connection`
:param kwds: keyword arguments for :class:`pymongo.connection.Connection`
'''
@@ -106,7 +112,7 @@ def connect(self, database, timezone=None, cache_size=0, *args, **kwds):
kwds['tz_aware'] = True
conn = Connection(*args, **kwds)
db = conn[database]
- return Session(db, timezone=timezone, safe=safe, cache_size=cache_size)
+ return Session(db, timezone=timezone, safe=safe, cache_size=cache_size, auto_ensure=auto_ensure)
def cache_write(self, obj, mongo_id=None):
if mongo_id is None:
@@ -212,13 +218,13 @@ def execute_query(self, query, session):
transaction, so any objects retrieved which are not in the cache
which would be updated when the transaction finishes will be
stale '''
- collection = self.db[query.type.get_collection_name()]
- for index in query.type.get_indexes():
- index.ensure(collection)
+ self.auto_ensure_indexes(query.type)
kwargs = dict()
if query.get_fields():
kwargs['fields'] = [str(f) for f in query.get_fields()]
+
+ collection = self.db[query.type.get_collection_name()]
cursor = collection.find(query.query, **kwargs)
if query.sort:
@@ -285,10 +291,9 @@ def execute_find_and_modify(self, fm_exp):
if self.in_transaction:
raise TransactionException('Cannot find and modify in a transaction.')
self.flush()
+ self.auto_ensure_indexes(fm_exp.query.type)
# assert len(fm_exp.update_data) > 0
collection = self.db[fm_exp.query.type.get_collection_name()]
- for index in fm_exp.query.type.get_indexes():
- index.ensure(collection)
kwargs = {
'query' : fm_exp.query.query,
'update' : fm_exp.update_data,
@@ -338,7 +343,16 @@ def get_indexes(self, cls):
`cls`. Index information is returned in the same format as *pymongo*.
'''
return self.db[cls.get_collection_name()].index_information()
-
+
+ def ensure_indexes(self, cls):
+ collection = self.db[cls.get_collection_name()]
+ for index in cls.get_indexes():
+ index.ensure(collection)
+
+ def auto_ensure_indexes(self, cls):
+ if self.auto_ensure:
+ self.ensure_indexes(cls)
+
def clear_queue(self, trans_id=None):
''' Clear the queue of database operations without executing any of
the pending operations'''
View
16 test/test_fields.py
@@ -3,6 +3,7 @@
from test.util import known_failure
from datetime import datetime
from bson.binary import Binary
+import re
# Field Tests
@@ -85,6 +86,21 @@ def string_value_test():
assert s.wrap('foo') == 'foo'
assert s.unwrap('bar') == 'bar'
+# RegEx String Tests
+IP_ADDR_RE = re.compile(r"^" + r"\.".join([r"([01]?\d\d?|2[0-4]\d|25[0-5])"]*4) + r"$")
+@raises(BadValueException)
+def regex_wrong_type_test():
+ RegExStringField(IP_ADDR_RE).wrap(4)
+
+@raises(BadValueException)
+def regex_no_match_test():
+ RegExStringField(IP_ADDR_RE).wrap('192.168.1.260')
+
+def regex_value_test():
+ s = RegExStringField(IP_ADDR_RE)
+ assert s.wrap('192.168.1.127') == '192.168.1.127'
+ assert s.unwrap('192.168.1.127') == '192.168.1.127'
+
# Bool Field
@raises(BadValueException)
def bool_wrong_type_test():
View
30 test/test_session.py
@@ -349,7 +349,6 @@ def test_update_docfield_list_extras():
else:
assert False
-
def test_update_list():
s = Session.connect('unit-testing')
s.clear_collection(TIntListDoc)
@@ -378,3 +377,32 @@ def test_update_list():
tFetched = s.query(TIntListDoc).one()
assert sorted([2,3]) == sorted(tFetched.intlist)
+
+def test_ensure_indexes():
+ s = Session.connect('unit-testing', auto_ensure=False)
+ s.db.drop_collection(TUnique.get_collection_name())
+
+ s.insert(TUnique(i=1))
+
+ indexes = s.get_indexes(TUnique)
+ assert len(indexes) == 1
+ assert "_id_" in indexes
+
+ s.ensure_indexes(TUnique)
+
+ indexes = s.get_indexes(TUnique)
+ assert len(indexes) == 2
+ assert "_id_" in indexes
+ assert "i_1" in indexes
+
+def test_auto_ensure_indexes():
+ s = Session.connect('unit-testing', auto_ensure=True)
+ s.db.drop_collection(TUnique.get_collection_name())
+
+ s.insert(TUnique(i=1))
+
+ indexes = s.get_indexes(TUnique)
+
+ assert len(indexes) == 2
+ assert "_id_" in indexes
+ assert "i_1" in indexes
View
2 test/test_update_expressions.py
@@ -42,7 +42,7 @@ def test_find_and_modify():
s.clear_collection(T, T2)
# insert
value = s.query(T).filter_by(i=12341).find_and_modify().set(i=12341).upsert().execute()
- assert value == {}, value
+ assert value is None or value == {}, value # Mongo 2.2 now returns None instead of an empty document
assert s.query(T).one().i == 12341
# update

0 comments on commit 5f5f4b2

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