Merge branch 'master' into deferred_fields

This commit is contained in:
blackbrrr
2010-01-14 11:39:09 -06:00
8 changed files with 177 additions and 40 deletions

View File

@@ -69,7 +69,8 @@ class ObjectIdField(BaseField):
"""
def to_python(self, value):
return unicode(value)
return value
# return unicode(value)
def to_mongo(self, value):
if not isinstance(value, pymongo.objectid.ObjectId):
@@ -246,6 +247,25 @@ class BaseDocument(object):
value = getattr(self, attr_name, None)
setattr(self, attr_name, value)
def validate(self):
"""Ensure that all fields' values are valid and that required fields
are present.
"""
# Get a list of tuples of field names and their current values
fields = [(field, getattr(self, name))
for name, field in self._fields.items()]
# Ensure that each field is matched to a valid value
for field, value in fields:
if value is not None:
try:
field.validate(value)
except (ValueError, AttributeError, AssertionError), e:
raise ValidationError('Invalid value for field of type "' +
field.__class__.__name__ + '"')
elif field.required:
raise ValidationError('Field "%s" is required' % field.name)
@classmethod
def _get_subclasses(cls):
"""Return a dictionary of all subclasses (found recursively).

View File

@@ -107,25 +107,6 @@ class Document(BaseDocument):
for field in self._fields:
setattr(self, field, obj[field])
def validate(self):
"""Ensure that all fields' values are valid and that required fields
are present.
"""
# Get a list of tuples of field names and their current values
fields = [(field, getattr(self, name))
for name, field in self._fields.items()]
# Ensure that each field is matched to a valid value
for field, value in fields:
if value is not None:
try:
field.validate(value)
except (ValueError, AttributeError, AssertionError), e:
raise ValidationError('Invalid value for field of type "' +
field.__class__.__name__ + '"')
elif field.required:
raise ValidationError('Field "%s" is required' % field.name)
@classmethod
def drop_collection(cls):
"""Drops the entire collection associated with this

View File

@@ -1,26 +1,37 @@
from base import BaseField, ObjectIdField, ValidationError
from document import Document, EmbeddedDocument
from connection import _get_db
import re
import pymongo
import datetime
import decimal
__all__ = ['StringField', 'IntField', 'FloatField', 'BooleanField',
'DateTimeField', 'EmbeddedDocumentField', 'ListField',
'ObjectIdField', 'ReferenceField', 'ValidationError']
'DateTimeField', 'EmbeddedDocumentField', 'ListField',
'ObjectIdField', 'ReferenceField', 'ValidationError',
'URLField', 'DecimalField']
URL_REGEX = re.compile(
r'^https?://'
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|'
r'localhost|'
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
r'(?::\d+)?'
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
class StringField(BaseField):
"""A unicode string field.
"""
def __init__(self, regex=None, max_length=None, **kwargs):
self.regex = re.compile(regex) if regex else None
self.max_length = max_length
super(StringField, self).__init__(**kwargs)
def to_python(self, value):
return unicode(value)
@@ -38,6 +49,25 @@ class StringField(BaseField):
return None
class URLField(BaseField):
"""A field that validates input as a URL.
"""
def __init__(self, verify_exists=True, **kwargs):
self.verify_exists = verify_exists
super(URLField, self).__init__(**kwargs)
def validate(self, value):
import urllib2
if self.verify_exists:
try:
request = urllib2.Request(value)
response = urllib2.urlopen(request)
except Exception, e:
raise ValidationError('This URL appears to be invalid: %s' % e)
class IntField(BaseField):
"""An integer field.
"""
@@ -45,12 +75,15 @@ class IntField(BaseField):
def __init__(self, min_value=None, max_value=None, **kwargs):
self.min_value, self.max_value = min_value, max_value
super(IntField, self).__init__(**kwargs)
def to_python(self, value):
return int(value)
def validate(self, value):
assert isinstance(value, (int, long))
try:
value = int(value)
except:
raise ValidationError('%s could not be converted to int' % value)
if self.min_value is not None and value < self.min_value:
raise ValidationError('Integer value is too small')
@@ -66,7 +99,7 @@ class FloatField(BaseField):
def __init__(self, min_value=None, max_value=None, **kwargs):
self.min_value, self.max_value = min_value, max_value
super(FloatField, self).__init__(**kwargs)
def to_python(self, value):
return float(value)
@@ -80,12 +113,41 @@ class FloatField(BaseField):
raise ValidationError('Float value is too large')
class DecimalField(BaseField):
"""A fixed-point decimal number field.
"""
def __init__(self, min_value=None, max_value=None, **kwargs):
self.min_value, self.max_value = min_value, max_value
super(DecimalField, self).__init__(**kwargs)
def to_python(self, value):
if not isinstance(value, basestring):
value = unicode(value)
return decimal.Decimal(value)
def validate(self, value):
if not isinstance(value, decimal.Decimal):
if not isinstance(value, basestring):
value = str(value)
try:
value = decimal.Decimal(value)
except Exception, exc:
raise ValidationError('Could not convert to decimal: %s' % exc)
if self.min_value is not None and value < self.min_value:
raise ValidationError('Decimal value is too small')
if self.max_value is not None and vale > self.max_value:
raise ValidationError('Decimal value is too large')
class BooleanField(BaseField):
"""A boolean field type.
.. versionadded:: 0.1.2
"""
def to_python(self, value):
return bool(value)
@@ -102,8 +164,8 @@ class DateTimeField(BaseField):
class EmbeddedDocumentField(BaseField):
"""An embedded document field. Only valid values are subclasses of
:class:`~mongoengine.EmbeddedDocument`.
"""An embedded document field. Only valid values are subclasses of
:class:`~mongoengine.EmbeddedDocument`.
"""
def __init__(self, document, **kwargs):
@@ -112,7 +174,7 @@ class EmbeddedDocumentField(BaseField):
'to an EmbeddedDocumentField')
self.document = document
super(EmbeddedDocumentField, self).__init__(**kwargs)
def to_python(self, value):
if not isinstance(value, self.document):
return self.document._from_son(value)
@@ -122,7 +184,7 @@ class EmbeddedDocumentField(BaseField):
return self.document.to_mongo(value)
def validate(self, value):
"""Make sure that the document instance is an instance of the
"""Make sure that the document instance is an instance of the
EmbeddedDocument subclass provided when the document was defined.
"""
# Using isinstance also works for subclasses of self.document
@@ -202,7 +264,7 @@ class ReferenceField(BaseField):
value = _get_db().dereference(value)
if value is not None:
instance._data[self.name] = self.document_type._from_son(value)
return super(ReferenceField, self).__get__(instance, owner)
def to_mongo(self, document):
@@ -222,7 +284,7 @@ class ReferenceField(BaseField):
collection = self.document_type._meta['collection']
return pymongo.dbref.DBRef(collection, id_)
def prepare_query_value(self, value):
return self.to_mongo(value)

View File

@@ -150,9 +150,10 @@ class QuerySet(object):
def __call__(self, *q_objs, **query):
"""Filter the selected documents by calling the
:class:`~mongoengine.QuerySet` with a query.
:class:`~mongoengine.queryset.QuerySet` with a query.
:param q_objs: :class:`~mongoengine.Q` objects to be used in the query
:param q_objs: :class:`~mongoengine.queryset.Q` objects to be used in
the query
:param query: Django-style query keyword arguments
"""
for q in q_objs:
@@ -162,7 +163,9 @@ class QuerySet(object):
return self
def filter(self, *q_objs, **query):
return self.__call__(**query)
"""An alias of :meth:`~mongoengine.queryset.QuerySet.__call__`
"""
return self.__call__(*q_objs, **query)
@property
def _collection(self):
@@ -333,7 +336,16 @@ class QuerySet(object):
"""
# Slice provided
if isinstance(key, slice):
self._cursor_obj = self._cursor[key]
try:
self._cursor_obj = self._cursor[key]
except IndexError, err:
# PyMongo raises an error if key.start == key.stop, catch it,
# bin it, kill it.
if key.start >=0 and key.stop >= 0 and key.step is None:
if key.start == key.stop:
self.limit(0)
return self
raise err
# Allow further QuerySet modifications to be performed
return self
# Integer index provided