Merge branch 'master' into 0.8
Conflicts: AUTHORS docs/changelog.rst mongoengine/__init__.py mongoengine/base.py mongoengine/fields.py python-mongoengine.spec tests/test_document.py tests/test_fields.py tests/test_queryset.py
This commit is contained in:
		| @@ -9,10 +9,12 @@ _document_registry = {} | ||||
|  | ||||
| def get_document(name): | ||||
|     doc = _document_registry.get(name, None) | ||||
|     if not doc and '.' in name: | ||||
|     if not doc: | ||||
|         # Possible old style name | ||||
|         end = name.split('.')[-1] | ||||
|         possible_match = [k for k in _document_registry.keys() if k == end] | ||||
|         single_end = name.split('.')[-1] | ||||
|         compound_end = '.%s' % single_end | ||||
|         possible_match = [k for k in _document_registry.keys() | ||||
|                           if k.endswith(compound_end) or k == single_end] | ||||
|         if len(possible_match) == 1: | ||||
|             doc = _document_registry.get(possible_match.pop(), None) | ||||
|     if not doc: | ||||
|   | ||||
| @@ -15,13 +15,23 @@ MONGOENGINE_SESSION_DB_ALIAS = getattr( | ||||
|     settings, 'MONGOENGINE_SESSION_DB_ALIAS', | ||||
|     DEFAULT_CONNECTION_NAME) | ||||
|  | ||||
| # a setting for the name of the collection used to store sessions | ||||
| MONGOENGINE_SESSION_COLLECTION = getattr( | ||||
|     settings, 'MONGOENGINE_SESSION_COLLECTION', | ||||
|     'django_session') | ||||
|  | ||||
| # a setting for whether session data is stored encoded or not | ||||
| MONGOENGINE_SESSION_DATA_ENCODE = getattr( | ||||
|     settings, 'MONGOENGINE_SESSION_DATA_ENCODE', | ||||
|     True) | ||||
|  | ||||
| class MongoSession(Document): | ||||
|     session_key = fields.StringField(primary_key=True, max_length=40) | ||||
|     session_data = fields.StringField() | ||||
|     session_data = fields.StringField() if MONGOENGINE_SESSION_DATA_ENCODE \ | ||||
|                                         else fields.DictField() | ||||
|     expire_date = fields.DateTimeField() | ||||
|  | ||||
|     meta = {'collection': 'django_session', | ||||
|     meta = {'collection': MONGOENGINE_SESSION_COLLECTION, | ||||
|             'db_alias': MONGOENGINE_SESSION_DB_ALIAS, | ||||
|             'allow_inheritance': False} | ||||
|  | ||||
| @@ -34,7 +44,10 @@ class SessionStore(SessionBase): | ||||
|         try: | ||||
|             s = MongoSession.objects(session_key=self.session_key, | ||||
|                                      expire_date__gt=datetime.now())[0] | ||||
|             return self.decode(force_unicode(s.session_data)) | ||||
|             if MONGOENGINE_SESSION_DATA_ENCODE: | ||||
|                 return self.decode(force_unicode(s.session_data)) | ||||
|             else: | ||||
|                 return s.session_data | ||||
|         except (IndexError, SuspiciousOperation): | ||||
|             self.create() | ||||
|             return {} | ||||
| @@ -57,7 +70,10 @@ class SessionStore(SessionBase): | ||||
|         if self.session_key is None: | ||||
|             self._session_key = self._get_new_session_key() | ||||
|         s = MongoSession(session_key=self.session_key) | ||||
|         s.session_data = self.encode(self._get_session(no_load=must_create)) | ||||
|         if MONGOENGINE_SESSION_DATA_ENCODE: | ||||
|             s.session_data = self.encode(self._get_session(no_load=must_create)) | ||||
|         else: | ||||
|             s.session_data = self._get_session(no_load=must_create) | ||||
|         s.expire_date = self.get_expiry_date() | ||||
|         try: | ||||
|             s.save(force_insert=must_create, safe=True) | ||||
|   | ||||
| @@ -149,6 +149,7 @@ class EmailField(StringField): | ||||
|     def validate(self, value): | ||||
|         if not EmailField.EMAIL_REGEX.match(value): | ||||
|             self.error('Invalid Mail-address: %s' % value) | ||||
|         super(EmailField, self).validate(value) | ||||
|  | ||||
|  | ||||
| class IntField(BaseField): | ||||
| @@ -782,7 +783,7 @@ class ReferenceField(BaseField): | ||||
|     def to_mongo(self, document): | ||||
|         if isinstance(document, DBRef): | ||||
|             if not self.dbref: | ||||
|                 return DBRef.id | ||||
|                 return document.id | ||||
|             return document | ||||
|         elif not self.dbref and isinstance(document, basestring): | ||||
|             return document | ||||
| @@ -1377,6 +1378,16 @@ class SequenceField(BaseField): | ||||
|                                              upsert=True) | ||||
|         return self.value_decorator(counter['next']) | ||||
|  | ||||
|     def get_sequence_name(self): | ||||
|         if self.sequence_name: | ||||
|             return self.sequence_name | ||||
|         owner = self.owner_document | ||||
|         if issubclass(owner, Document): | ||||
|             return owner._get_collection_name() | ||||
|         else: | ||||
|             return ''.join('_%s' % c if c.isupper() else c | ||||
|                             for c in owner._class_name).strip('_').lower() | ||||
|  | ||||
|     def __get__(self, instance, owner): | ||||
|         value = super(SequenceField, self).__get__(instance, owner) | ||||
|         if value is None and instance._initialised: | ||||
|   | ||||
| @@ -58,6 +58,8 @@ class QuerySet(object): | ||||
|         self._read_preference = None | ||||
|         self._iter = False | ||||
|         self._scalar = [] | ||||
|         self._as_pymongo = False | ||||
|         self._as_pymongo_coerce = False | ||||
|  | ||||
|         # If inheritance is allowed, only return instances and instances of | ||||
|         # subclasses of the class being used | ||||
| @@ -178,11 +180,13 @@ class QuerySet(object): | ||||
|             if self._where_clause: | ||||
|                 self._cursor_obj.where(self._where_clause) | ||||
|  | ||||
|             # apply default ordering | ||||
|             if self._ordering: | ||||
|                 # Apply query ordering | ||||
|                 self._cursor_obj.sort(self._ordering) | ||||
|             elif self._document._meta['ordering']: | ||||
|                 # Otherwise, apply the ordering from the document model | ||||
|                 self.order_by(*self._document._meta['ordering']) | ||||
|                 self._cursor_obj.sort(self._ordering) | ||||
|  | ||||
|             if self._limit is not None: | ||||
|                 self._cursor_obj.limit(self._limit - (self._skip or 0)) | ||||
| @@ -328,7 +332,7 @@ class QuerySet(object): | ||||
|                 msg = ("Some documents inserted aren't instances of %s" | ||||
|                         % str(self._document)) | ||||
|                 raise OperationError(msg) | ||||
|             if doc.pk: | ||||
|             if doc.pk and not doc._created: | ||||
|                 msg = "Some documents have ObjectIds use doc.update() instead" | ||||
|                 raise OperationError(msg) | ||||
|             raw.append(doc.to_mongo()) | ||||
| @@ -388,6 +392,9 @@ class QuerySet(object): | ||||
|             for doc in docs: | ||||
|                 doc_map[doc['_id']] = self._get_scalar( | ||||
|                         self._document._from_son(doc)) | ||||
|         elif self._as_pymongo: | ||||
|             for doc in docs: | ||||
|                 doc_map[doc['_id']] = self._get_as_pymongo(doc) | ||||
|         else: | ||||
|             for doc in docs: | ||||
|                 doc_map[doc['_id']] = self._document._from_son(doc) | ||||
| @@ -404,6 +411,9 @@ class QuerySet(object): | ||||
|             if self._scalar: | ||||
|                 return self._get_scalar(self._document._from_son( | ||||
|                         self._cursor.next())) | ||||
|             if self._as_pymongo: | ||||
|                 return self._get_as_pymongo(self._cursor.next()) | ||||
|  | ||||
|             return self._document._from_son(self._cursor.next()) | ||||
|         except StopIteration, e: | ||||
|             self.rewind() | ||||
| @@ -592,6 +602,8 @@ class QuerySet(object): | ||||
|             if self._scalar: | ||||
|                 return self._get_scalar(self._document._from_son( | ||||
|                         self._cursor[key])) | ||||
|             if self._as_pymongo: | ||||
|                 return self._get_as_pymongo(self._cursor.next()) | ||||
|             return self._document._from_son(self._cursor[key]) | ||||
|         raise AttributeError | ||||
|  | ||||
| @@ -714,7 +726,7 @@ class QuerySet(object): | ||||
|             key_list.append((key, direction)) | ||||
|  | ||||
|         self._ordering = key_list | ||||
|         self._cursor.sort(key_list) | ||||
|  | ||||
|         return self | ||||
|  | ||||
|     def explain(self, format=False): | ||||
| @@ -887,6 +899,48 @@ class QuerySet(object): | ||||
|  | ||||
|         return tuple(data) | ||||
|  | ||||
|     def _get_as_pymongo(self, row): | ||||
|         # Extract which fields paths we should follow if .fields(...) was | ||||
|         # used. If not, handle all fields. | ||||
|         if not getattr(self, '__as_pymongo_fields', None): | ||||
|             self.__as_pymongo_fields = [] | ||||
|             for field in self._loaded_fields.fields - set(['_cls', '_id', '_types']): | ||||
|                 self.__as_pymongo_fields.append(field) | ||||
|                 while '.' in field: | ||||
|                     field, _ = field.rsplit('.', 1) | ||||
|                     self.__as_pymongo_fields.append(field) | ||||
|  | ||||
|         all_fields = not self.__as_pymongo_fields | ||||
|  | ||||
|         def clean(data, path=None): | ||||
|             path = path or '' | ||||
|  | ||||
|             if isinstance(data, dict): | ||||
|                 new_data = {} | ||||
|                 for key, value in data.iteritems(): | ||||
|                     new_path = '%s.%s' % (path, key) if path else key | ||||
|                     if all_fields or new_path in self.__as_pymongo_fields: | ||||
|                         new_data[key] = clean(value, path=new_path) | ||||
|                 data = new_data | ||||
|             elif isinstance(data, list): | ||||
|                 data = [clean(d, path=path) for d in data] | ||||
|             else: | ||||
|                 if self._as_pymongo_coerce: | ||||
|                     # If we need to coerce types, we need to determine the | ||||
|                     # type of this field and use the corresponding .to_python(...) | ||||
|                     from mongoengine.fields import EmbeddedDocumentField | ||||
|                     obj = self._document | ||||
|                     for chunk in path.split('.'): | ||||
|                         obj = getattr(obj, chunk, None) | ||||
|                         if obj is None: | ||||
|                             break | ||||
|                         elif isinstance(obj, EmbeddedDocumentField): | ||||
|                             obj = obj.document_type | ||||
|                     if obj and data is not None: | ||||
|                         data = obj.to_python(data) | ||||
|             return data | ||||
|         return clean(row) | ||||
|  | ||||
|     def scalar(self, *fields): | ||||
|         """Instead of returning Document instances, return either a specific | ||||
|         value or a tuple of values in order. | ||||
| @@ -909,6 +963,16 @@ class QuerySet(object): | ||||
|         """An alias for scalar""" | ||||
|         return self.scalar(*fields) | ||||
|  | ||||
|     def as_pymongo(self, coerce_types=False): | ||||
|         """Instead of returning Document instances, return raw values from | ||||
|         pymongo. | ||||
|  | ||||
|         :param coerce_type: Field types (if applicable) would be use to coerce types. | ||||
|         """ | ||||
|         self._as_pymongo = True | ||||
|         self._as_pymongo_coerce = coerce_types | ||||
|         return self | ||||
|  | ||||
|     def _sub_js_fields(self, code): | ||||
|         """When fields are specified with [~fieldname] syntax, where | ||||
|         *fieldname* is the Python name of a field, *fieldname* will be | ||||
|   | ||||
		Reference in New Issue
	
	Block a user