Esempio n. 1
0
    def __setattr__(self, name, value):
        # Handle dynamic data only if an initialised dynamic document
        if self._dynamic and not self._dynamic_lock:

            field = None
            if not hasattr(self, name) and not name.startswith('_'):
                field = BaseDynamicField(db_field=name)
                field.name = name
                self._dynamic_fields[name] = field

            if not name.startswith('_'):
                value = self.__expand_dynamic_values(name, value)

            # Handle marking data as changed
            if name in self._dynamic_fields:
                self._data[name] = value
                if hasattr(self, '_changed_fields'):
                    self._mark_as_changed(name)

        # Handle None values for required fields
        if value is None and name in getattr(self, '_fields', {}):
            self._data[name] = value
            if hasattr(self, '_changed_fields'):
                self._mark_as_changed(name)
            return

        if not self._created and name in self._meta.get('shard_key', tuple()):
            from queryset import OperationError
            raise OperationError("Shard Keys are immutable. Tried to update %s" % name)

        super(BaseDocument, self).__setattr__(name, value)
Esempio n. 2
0
    def save(self, safe=True, force_insert=False, validate=True):
        """Save the :class:`~mongoengine.Document` to the database. If the
        document already exists, it will be updated, otherwise it will be
        created.

        If ``safe=True`` and the operation is unsuccessful, an 
        :class:`~mongoengine.OperationError` will be raised.

        :param safe: check if the operation succeeded before returning
        :param force_insert: only try to create a new document, don't allow 
            updates of existing documents
        :param validate: validates the document; set to ``False`` for skiping
        """
        if validate:
            self.validate()
        doc = self.to_mongo()
        try:
            collection = self.__class__.objects._collection
            if force_insert:
                object_id = collection.insert(doc, safe=safe)
            else:
                object_id = collection.save(doc, safe=safe)
        except pymongo.errors.OperationFailure, err:
            message = 'Could not save document (%s)'
            if u'duplicate key' in unicode(err):
                message = u'Tried to save duplicate unique keys (%s)'
            raise OperationError(message % unicode(err))
Esempio n. 3
0
 def to_dbref(self):
     """Returns an instance of :class:`~bson.dbref.DBRef` useful in
     `__raw__` queries."""
     if not self.pk:
         msg = "Only saved documents can have a valid dbref"
         raise OperationError(msg)
     return DBRef(self.__class__._get_collection_name(), self.pk)
Esempio n. 4
0
    def update(self, **kwargs):
        """Performs an update on the :class:`~mongoengine.Document`
        A convenience wrapper to :meth:`~mongoengine.QuerySet.update`.

        Raises :class:`OperationError` if called on an object that has not yet
        been saved.
        """
        if not self.pk:
            raise OperationError('attempt to update a document not yet saved')

        return self.__class__.objects(pk=self.pk).update_one(**kwargs)
Esempio n. 5
0
    def delete(self, safe=False):
        """Delete the :class:`~mongoengine.Document` from the database. This
        will only take effect if the document has been previously saved.

        :param safe: check if the operation succeeded before returning
        """
        id_field = self._meta['id_field']
        object_id = self._fields[id_field].to_mongo(self[id_field])
        try:
            self.__class__.objects(**{id_field: object_id}).delete(safe=safe)
        except pymongo.errors.OperationFailure, err:
            message = u'Could not delete document (%s)' % err.message
            raise OperationError(message)
Esempio n. 6
0
    def delete(self, safe=False):
        """Delete the :class:`~mongoengine.Document` from the database. This
        will only take effect if the document has been previously saved.

        :param safe: check if the operation succeeded before returning
        """
        signals.pre_delete.send(self.__class__, document=self)

        try:
            self.__class__.objects(pk=self.pk).delete(safe=safe)
        except pymongo.errors.OperationFailure, err:
            message = u'Could not delete document (%s)' % err.message
            raise OperationError(message)
Esempio n. 7
0
    def update(self, **kwargs):
        """Performs an update on the :class:`~mongoengine.Document`
        A convenience wrapper to :meth:`~mongoengine.QuerySet.update`.

        Raises :class:`OperationError` if called on an object that has not yet
        been saved.
        """
        if not self.pk:
            raise OperationError('attempt to update a document not yet saved')

        # Need to add shard key to query, or you get an error
        select_dict = {'pk': self.pk}
        shard_key = self.__class__._meta.get('shard_key', tuple())
        for k in shard_key:
            select_dict[k] = getattr(self, k)
        return self.__class__.objects(**select_dict).update_one(**kwargs)
Esempio n. 8
0
    def reload(self, max_depth=1):
        """Reloads all attributes from the database.

        .. versionadded:: 0.1.2
        .. versionchanged:: 0.6  Now chainable
        """
        id_field = self._meta['id_field']
        obj = self.__class__.objects(**{
            id_field: self[id_field]
        }).limit(1).select_related(max_depth=max_depth)
        if obj:
            obj = obj[0]
        else:
            msg = "Reloaded document has been deleted"
            raise OperationError(msg)
        for field in self._fields:
            setattr(self, field, self._reload(field, obj[field]))
        if self._dynamic:
            for name in self._dynamic_fields.keys():
                setattr(self, name, self._reload(name, obj._data[name]))
        self._changed_fields = obj._changed_fields
        return obj
Esempio n. 9
0
    def save(self,
             safe=True,
             force_insert=False,
             validate=True,
             write_options=None,
             cascade=None,
             cascade_kwargs=None,
             _refs=None):
        """Save the :class:`~mongoengine.Document` to the database. If the
        document already exists, it will be updated, otherwise it will be
        created.

        If ``safe=True`` and the operation is unsuccessful, an
        :class:`~mongoengine.OperationError` will be raised.

        :param safe: check if the operation succeeded before returning
        :param force_insert: only try to create a new document, don't allow
            updates of existing documents
        :param validate: validates the document; set to ``False`` to skip.
        :param write_options: Extra keyword arguments are passed down to
                :meth:`~pymongo.collection.Collection.save` OR
                :meth:`~pymongo.collection.Collection.insert`
                which will be used as options for the resultant ``getLastError`` command.
                For example, ``save(..., write_options={w: 2, fsync: True}, ...)`` will
                wait until at least two servers have recorded the write and will force an
                fsync on each server being written to.
        :param cascade: Sets the flag for cascading saves.  You can set a default by setting
            "cascade" in the document __meta__
        :param cascade_kwargs: optional kwargs dictionary to be passed throw to cascading saves
        :param _refs: A list of processed references used in cascading saves

        .. versionchanged:: 0.5
            In existing documents it only saves changed fields using set / unset
            Saves are cascaded and any :class:`~bson.dbref.DBRef` objects
            that have changes are saved as well.
        .. versionchanged:: 0.6
            Cascade saves are optional = defaults to True, if you want fine grain
            control then you can turn off using document meta['cascade'] = False
            Also you can pass different kwargs to the cascade save using cascade_kwargs
            which overwrites the existing kwargs with custom values

        """
        signals.pre_save.send(self.__class__, document=self)

        if validate:
            self.validate()

        if not write_options:
            write_options = {}

        doc = self.to_mongo()

        created = force_insert or '_id' not in doc

        try:
            collection = self.__class__.objects._collection
            if created:
                if force_insert:
                    object_id = collection.insert(doc,
                                                  safe=safe,
                                                  **write_options)
                else:
                    object_id = collection.save(doc,
                                                safe=safe,
                                                **write_options)
            else:
                object_id = doc['_id']
                updates, removals = self._delta()
                # Need to add shard key to query, or you get an error
                select_dict = {'_id': object_id}
                shard_key = self.__class__._meta.get('shard_key', tuple())
                for k in shard_key:
                    actual_key = self._db_field_map.get(k, k)
                    select_dict[actual_key] = doc[actual_key]

                upsert = self._created
                if updates:
                    collection.update(select_dict, {"$set": updates},
                                      upsert=upsert,
                                      safe=safe,
                                      **write_options)
                if removals:
                    collection.update(select_dict, {"$unset": removals},
                                      upsert=upsert,
                                      safe=safe,
                                      **write_options)

            cascade = self._meta.get('cascade',
                                     True) if cascade is None else cascade
            if cascade:
                kwargs = {
                    "safe": safe,
                    "force_insert": force_insert,
                    "validate": validate,
                    "write_options": write_options,
                    "cascade": cascade
                }
                if cascade_kwargs:  # Allow granular control over cascades
                    kwargs.update(cascade_kwargs)
                kwargs['_refs'] = _refs
                #self._changed_fields = []
                self.cascade_save(**kwargs)

        except pymongo.errors.OperationFailure, err:
            message = 'Could not save document (%s)'
            if u'duplicate key' in unicode(err):
                message = u'Tried to save duplicate unique keys (%s)'
            raise OperationError(message % unicode(err))
Esempio n. 10
0
    def save(self, safe=True, force_insert=False, validate=True, write_options=None, _refs=None):
        """Save the :class:`~mongoengine.Document` to the database. If the
        document already exists, it will be updated, otherwise it will be
        created.

        If ``safe=True`` and the operation is unsuccessful, an
        :class:`~mongoengine.OperationError` will be raised.

        :param safe: check if the operation succeeded before returning
        :param force_insert: only try to create a new document, don't allow
            updates of existing documents
        :param validate: validates the document; set to ``False`` to skip.
        :param write_options: Extra keyword arguments are passed down to
                :meth:`~pymongo.collection.Collection.save` OR
                :meth:`~pymongo.collection.Collection.insert`
                which will be used as options for the resultant ``getLastError`` command.
                For example, ``save(..., w=2, fsync=True)`` will wait until at least two servers
                have recorded the write and will force an fsync on each server being written to.

        .. versionchanged:: 0.5
            In existing documents it only saves changed fields using set / unset
            Saves are cascaded and any :class:`~pymongo.dbref.DBRef` objects
            that have changes are saved as well.
        """
        from fields import ReferenceField, GenericReferenceField

        signals.pre_save.send(self.__class__, document=self)

        if validate:
            self.validate()

        if not write_options:
            write_options = {}

        doc = self.to_mongo()

        created = '_id' in doc
        creation_mode = force_insert or not created
        try:
            collection = self.__class__.objects._collection
            if creation_mode:
                if force_insert:
                    object_id = collection.insert(doc, safe=safe, **write_options)
                else:
                    object_id = collection.save(doc, safe=safe, **write_options)
            else:
                object_id = doc['_id']
                updates, removals = self._delta()
                if updates:
                    collection.update({'_id': object_id}, {"$set": updates}, upsert=False, safe=safe, **write_options)
                if removals:
                    collection.update({'_id': object_id}, {"$unset": removals}, upsert=False, safe=safe, **write_options)

            # Save any references / generic references
            _refs = _refs or []
            for name, cls in self._fields.items():
                if isinstance(cls, (ReferenceField, GenericReferenceField)):
                    ref = getattr(self, name)
                    if ref and str(ref) not in _refs:
                        _refs.append(str(ref))
                        ref.save(safe=safe, force_insert=force_insert,
                                 validate=validate, write_options=write_options,
                                 _refs=_refs)

        except pymongo.errors.OperationFailure, err:
            message = 'Could not save document (%s)'
            if u'duplicate key' in unicode(err):
                message = u'Tried to save duplicate unique keys (%s)'
            raise OperationError(message % unicode(err))