API reference¶
Validators¶
- class monk.validators.All(specs, default=None, first_is_default=False)[source]¶
Requires that the value passes all nested validators.
- error_class¶
alias of AtLeastOneFailed
- class monk.validators.Any(specs, default=None, first_is_default=False)[source]¶
Requires that the value passes at least one of nested validators.
- error_class¶
alias of AllFailed
- class monk.validators.Exists(default=None)[source]¶
Requires that the value exists. Obviously this only makes sense in special cases like dictionary keys; otherwise there’s simply nothing to validate. Note that this is not a check against None or False.
- class monk.validators.IsA(expected_type, default=None)[source]¶
Requires that the value is an instance of given type.
- class monk.validators.Equals(expected_value)[source]¶
Requires that the value equals given expected value.
- class monk.validators.Contains(expected_value)[source]¶
Requires that the value contains given expected value.
- class monk.validators.InRange(min=None, max=None, default=NotImplemented)[source]¶
Requires that the numeric value is in given boundaries.
- class monk.validators.Length(min=None, max=None, default=NotImplemented)[source]¶
Requires that the value length is in given boundaries.
- class monk.validators.ListOfAll(validator, default=None)[source]¶
Requires that the value is a list which items match given validator. Usage:
>>> v = ListOfAll(IsA(int) | IsA(str)) >>> v([123, 'hello']) >>> v([123, 'hello', 5.5]) Traceback (most recent call last): ... ValidationError: item #2: must be int or must be str
- error_class¶
alias of AtLeastOneFailed
- class monk.validators.ListOfAny(validator, default=None)[source]¶
Same as ListOfAll but tolerates invalid items as long as there is at least one valid among them.
- error_class¶
alias of AllFailed
- class monk.validators.DictOf(pairs)[source]¶
Requires that the value is a dict which items match given patterns. Usage:
>>> v = DictOf([ ... # key "name" must exist; its value must be a `str` ... (Equals('name'), IsA(str)), ... # key "age" may not exist; its value must be an `int` ... (Equals('age') | ~Exists(), IsA(int)), ... # there may be other `str` keys with `str` or `int` values ... (IsA(str), IsA(str) | IsA(int)), ... ]) >>> v({'name': 'John'}) >>> v({'name': 'John', 'age': 25}) >>> v({'name': 'John', 'age': 25.5}) Traceback (most recent call last): ... DictValueError: 'age' value must be int >>> v({'name': 'John', 'age': 25, 'note': 'custom field'}) >>> v({'name': 'John', 'age': 25, 'note': 5.5}) Traceback (most recent call last): ... DictValueError: 'note' value must be str or must be int
Note that this validator supports Exists to mark keys that can be missing.
Shortcuts¶
- monk.shortcuts.nullable(spec)[source]¶
Returns a validator which allows the value to be None.
>>> nullable(str) == IsA(str) | Equals(None) True
- monk.shortcuts.optional(spec)[source]¶
Returns a validator which allows the value to be missing.
>>> optional(str) == IsA(str) | ~Exists() True >>> optional('foo') == IsA(str, default='foo') | ~Exists() True
Note that you should normally opt_key() to mark dictionary keys as optional.
- monk.shortcuts.opt_key(spec)[source]¶
Returns a validator which allows the value to be missing. Similar to optional() but wraps a string in Equals instead of IsA. Intended for dictionary keys.
>>> opt_key(str) == IsA(str) | ~Exists() True >>> opt_key('foo') == Equals('foo') | ~Exists() True
Helpers¶
- monk.helpers.validate(spec, value)[source]¶
Validates given value against given specification. Raises an exception if the value is invalid. Always returns None.
In fact, it’s just a very thin wrapper around the validators. These three expressions are equal:
IsA(str)('foo') translate(str)('foo') validate(str, 'foo')
Spec : a validator instance or any value digestible by translate(). Value : any value including complex structures. Can raise:
- MissingKey
- if a dictionary key is in the spec but not in the value. This applies to root and nested dictionaries.
- InvalidKey
- if a dictionary key is the value but not not in the spec.
- StructureSpecificationError
- if errors were found in spec.
- monk.helpers.walk_dict(data)[source]¶
Generates pairs (keys, value) for each item in given dictionary, including nested dictionaries. Each pair contains:
- keys
- a tuple of 1..n keys, e.g. ('foo',) for a key on root level or ('foo', 'bar') for a key in a nested dictionary.
- value
- the value of given key or None if it is a nested dictionary and therefore can be further unwrapped.
Data manipulation¶
- monk.manipulation.merge_defaults(spec, value)[source]¶
Returns a copy of value recursively updated to match the spec:
- New values are added whenever possible (including nested ones).
- Existing values are never changed or removed.
- Exception: container values (lists, dictionaries) may be populated; see respective merger functions for details.
The result may not pass validation against the spec in the following cases:
- a required value is missing and the spec does not provide defaults;
- an existing value is invalid.
The business logic is as follows:
- if value is empty, use default value from spec;
- if value is present or spec has no default value:
- if spec datatype is present as a key in mergers, use the respective merger function to obtain the value;
- if no merger is assigned to the datatype, use fallback function.
See documentation on concrete merger functions for further details.
Spec : A “natural” or “verbose” spec. Value : The value to merge into the spec. Examples:
>>> merge_defaults('foo', None) 'foo' >>> merge_defaults('foo', 'bar') 'bar' >>> merge_defaults({'a': 'foo'}, {}) {'a': 'foo'} >>> merge_defaults({'a': [{'b': 123}]}, ... {'a': [{'b': None}, ... {'x': 0}]}) {'a': [{'b': 123}, {'b': 123, 'x': 0}]}
- monk.manipulation.normalize_to_list(value)[source]¶
Converts given value to a list as follows:
- [x] → [x]
- x → [x]
- monk.manipulation.normalize_list_of_dicts(value, default_key, default_value=<class monk.manipulation.UNDEFINED at 0x7f2017694460>)[source]¶
Converts given value to a list of dictionaries as follows:
- [{...}] → [{...}]
- {...} → [{...}]
- 'xyz' → [{default_key: 'xyz'}]
- None → [{default_key: default_value}] (if specified)
- None → []
Parameters: default_value – only Unicode, i.e. str in Python 3.x and only unicode in Python 2.x
Modeling¶
DB-agnostic helpers to build powerful ODMs.
- class monk.modeling.DotExpandedDictMixin[source]¶
Makes the dictionary dot-expandable by exposing dictionary members via __getattr__ and __setattr__ in addition to __getitem__ and __setitem__. For example, this is the default API:
data = {'foo': {'bar': 0 } } print data['foo']['bar'] data['foo']['bar'] = 123
This mixin adds the following API:
print data.foo.bar data.foo.bar = 123
Nested dictionaries are converted to dot-expanded ones on adding.
Exceptions¶
- exception monk.errors.AllFailed[source]¶
Raised when at least one validator was expected to pass but none did.
- exception monk.errors.AtLeastOneFailed[source]¶
Raised when all validators were expected to pas but at least one didn’t.
- exception monk.errors.CombinedValidationError[source]¶
Raised when a combination of specs has failed validation.
- exception monk.errors.DictValueError[source]¶
Raised when dictionary value fails validation. Used to detect nested errors in order to format the human-readable messages unambiguously.
- exception monk.errors.InvalidKeys[source]¶
Raised whan the value dictionary contains an unexpected key.
- exception monk.errors.MissingKeys[source]¶
Raised when a required dictionary key is missing from the value dict.
- exception monk.errors.NoDefaultValue[source]¶
Raised when the validator could not produce a default value.
MongoDB integration¶
This module combines Monk’s modeling and validation capabilities with MongoDB.
Declaring indexes¶
Let’s declare a model with indexes:
from monk.mongo import Document
class Item(Document):
structure = dict(text=unicode, slug=unicode)
indexes = dict(text=None, slug=dict(unique=True))
Now create a model instance:
item = Item(text=u'foo', slug=u'bar')
Save it and make sure the indexes are created:
item.save(db)
The last line is roughly equivalent to:
collection = db[item.collection]
collection.ensure_index('text')
collection.ensure_index('slug', unique=True)
collection.save(dict(item)) # also validation, transformation, etc.
- class monk.mongo.Document(*args, **kwargs)[source]¶
A structured dictionary that is bound to MongoDB and supports dot notation for access to items.
Inherits features from:
- dict (builtin),
- TypedDictReprMixin,
- DotExpandedDictMixin,
- StructuredDictMixin and
- MongoBoundDictMixin.
- class monk.mongo.MongoBoundDictMixin[source]¶
Adds MongoDB-specific features to the dictionary.
- collection¶
Collection name.
- indexes¶
(TODO)
- classmethod find(db, *args, **kwargs)[source]¶
Returns a MongoResultSet object.
Example:
items = Item.find(db, {'title': u'Hello'})
Note
The arguments are those of pymongo collection’s find method. A frequent error is to pass query key/value pairs as keyword arguments. This is wrong. In most cases you will want to pass a dictionary (“query spec”) as the first positional argument.
- classmethod get_one(db, *args, **kwargs)[source]¶
Returns an object that corresponds to given query or None.
Example:
item = Item.get_one(db, {'title': u'Hello'})
- remove(db)[source]¶
Removes the object from given database. Usage:
item = Item.get_one(db) item.remove(db)
Collection name is taken from MongoBoundDictMixin.collection.
- save(db)[source]¶
Saves the object to given database. Usage:
item = Item(title=u'Hello') item.save(db)
Collection name is taken from MongoBoundDictMixin.collection.
- class monk.mongo.MongoResultSet(cursor, wrapper)[source]¶
A wrapper for pymongo cursor that wraps each item using given function or class.
Warning
This class does not introduce caching. Iterating over results exhausts the cursor.
- ids()[source]¶
Returns a generator with identifiers of objects in set. These expressions are equivalent:
ids = (item.id for item in result_set) ids = result_set.ids()
Warning
This method exhausts the cursor, so an attempt to iterate over results after calling this method will fail. The results are not cached.