class MongoModel( with_metaclass(TopLevelMongoModelMetaclass, TopLevelMongoModel)): """Base class for all top-level models. A MongoModel definition typically includes a number of field instances and possibly a ``Meta`` class attribute that provides metadata or settings specific to the model. MongoModels can be instantiated either with positional or keyword arguments. Positional arguments are bound to the fields in the order the fields are defined on the model. Keyword argument names are the same as the names of the fields:: from pymongo.read_preferences import ReadPreference class User(MongoModel): email = fields.EmailField(primary_key=True) name = fields.CharField() class Meta: # Read from secondaries. read_preference = ReadPreference.SECONDARY # Instantiate User using positional arguments: jane = User('*****@*****.**', 'Jane') # Keyword arguments: roy = User(name='Roy', email='*****@*****.**') Note that :func:`~pymodm.connection.connect` has to be called (defining the respective connection alias, if any) before any :class:`~pymodm.MongoModel` can be used with that alias. If ``indexes`` is defined on ``Meta``, then this has to be before the MongoModel class is evaluated. .. _metadata-attributes: The following metadata attributes are available: - `connection_alias`: The alias of the connection to use for the model. - `collection_name`: The name of the collection to use. By default, this is the same name as the model, converted to snake case. - `codec_options`: An instance of :class:`~bson.codec_options.CodecOptions` to use for reading and writing documents of this model type. - `final`: Whether to restrict inheritance on this model. If ``True``, the ``_cls`` field will not be stored in the document. ``False`` by default. - `cascade`: If ``True``, save all :class:`~pymodm.MongoModel` instances this object references when :meth:`~pymodm.MongoModel.save` is called on this object. - `read_preference`: The :class:`~pymongo.read_preferences.ReadPreference` to use when reading documents. - `read_concern`: The :class:`~pymongo.read_concern.ReadConcern` to use when reading documents. - `write_concern`: The :class:`~pymongo.write_concern.WriteConcern` to use for write operations. - `indexes`: This is a list of :class:`~pymongo.operations.IndexModel` instances that describe the indexes that should be created for this model. Indexes are created when the class definition is evaluated. - `ignore_unknown_fields`: If ``True``, fields that aren't defined in the model will be ignored when parsing documents from MongoDB, such as in :meth:`~pymodm.MongoModel.from_document`. By default, unknown fields will cause a ``ValueError`` to be raised. Note that with this option enabled, calling :meth:`~pymodm.MongoModel.save` will erase these fields for that model instance. .. note:: Creating an instance of MongoModel does not create a document in the database. """ pass
class EmbeddedMongoModel(with_metaclass(MongoModelMetaclass, MongoModelBase)): """Base class for models that represent embedded documents.""" pass
class MongoModel(with_metaclass(TopLevelMongoModelMetaclass, MongoModelBase)): """Base class for all top-level models. A MongoModel definition typically includes a number of field instances and possibly a ``Meta`` class attribute that provides metadata or settings specific to the model. MongoModels can be instantiated either with positional or keyword arguments. Positional arguments are bound to the fields in the order the fields are defined on the model. Keyword argument names are the same as the names of the fields:: from pymongo.read_preferences import ReadPreference class User(MongoModel): email = fields.EmailField(primary_key=True) name = fields.CharField() class Meta: # Read from secondaries. read_preference = ReadPreference.SECONDARY # Instantiate User using positional arguments: jane = User('*****@*****.**', 'Jane') # Keyword arguments: roy = User(name='Roy', email='*****@*****.**') Note that :func:`~pymodm.connection.connect` has to be called (defining the respective connection alias, if any) before any :class:`~pymodm.MongoModel` can be used with that alias. If ``indexes`` is defined on ``Meta``, then this has to be before the MongoModel class is evaluated. .. _metadata-attributes: The following metadata attributes are available: - `connection_alias`: The alias of the connection to use for the moel. - `collection_name`: The name of the collection to use. By default, this is the same name as the model, converted to snake case. - `codec_options`: An instance of :class:`~bson.codec_options.CodecOptions` to use for reading and writing documents of this model type. - `final`: Whether to restrict inheritance on this model. If ``True``, the ``_cls`` field will not be stored in the document. ``False`` by default. - `cascade`: If ``True``, save all :class:`~pymodm.MongoModel` instances this object references when :meth:`~pymodm.MongoModel.save` is called on this object. - `read_preference`: The :class:`~pymongo.read_preferences.ReadPreference` to use when reading documents. - `read_concern`: The :class:`~pymongo.read_concern.ReadConcern` to use when reading documents. - `write_concern`: The :class:`~pymongo.write_concern.WriteConcern` to use for write operations. - `indexes`: This is a list of :class:`~pymongo.operations.IndexModel` instances that describe the indexes that should be created for this model. Indexes are created when the class definition is evaluated. .. note:: Creating an instance of MongoModel does not create a document in the database. """ @classmethod def register_delete_rule(cls, related_model, related_field, rule): """Specify what to do when an instance of this class is deleted. :parameters: - `related_model`: The class that references this class. - `related_field`: The name of the field in ``related_model`` that references this class. - `rule`: The delete rule. See :class:`~pymodm.fields.ReferenceField` for details. """ cls._mongometa.delete_rules[(related_model, related_field)] = rule @property def pk(self): """An alias for the primary key (called `_id` in MongoDB).""" if self._mongometa.pk is not None: return getattr(self, self._mongometa.pk.attname) @pk.setter def pk(self, value): if self._mongometa.pk is None: raise ValueError('No primary key set for %s' % self._mongometa.object_name) setattr(self, self._mongometa.pk.attname, value) @property def _qs(self): if not hasattr(self, '__queryset'): self.__queryset = None if (self.__queryset is None and not self._mongometa.pk.is_undefined(self)): self.__queryset = self.__class__._default_manager.raw( {'_id': self._mongometa.pk.to_mongo(self.pk)}) return self.__queryset def save(self, cascade=None, full_clean=True, force_insert=False): """Save this document into MongoDB. If there is no value for the primary key on this Model instance, the instance will be inserted into MongoDB. Otherwise, the entire document will be replaced with this version (upserting if necessary). :parameters: - `cascade`: If ``True``, all dereferenced MongoModels contained in this Model instance will also be saved. - `full_clean`: If ``True``, the :meth:`~pymodm.MongoModel.full_clean` method will be called before persisting this object. - `force_insert`: If ``True``, always do an insert instead of a replace. In this case, `save` will raise :class:`~pymongo.errors.DuplicateKeyError` if a document already exists with the same primary key. :returns: This object, with the `pk` property filled in if it wasn't already. """ cascade = validate_boolean_or_none('cascade', cascade) full_clean = validate_boolean('full_clean', full_clean) force_insert = validate_boolean('force_insert', force_insert) if full_clean: self.full_clean() if cascade or (self._mongometa.cascade and cascade is not False): for field_name in self: for referenced_object in self._find_referenced_objects( getattr(self, field_name)): referenced_object.save() if force_insert or self._mongometa.pk.is_undefined(self): result = self._mongometa.collection.insert_one(self.to_son()) self.pk = result.inserted_id else: result = self._mongometa.collection.replace_one( {'_id': self._mongometa.pk.to_mongo(self.pk)}, self.to_son(), upsert=True) return self def delete(self): """Delete this object from MongoDB.""" self._qs.delete() def is_valid(self): """Return ``True`` if the data in this Model is valid. This method runs the :meth:`~pymodm.MongoModel.full_clean` method and returns ``True`` if no ValidationError was raised. """ try: self.full_clean() except ValidationError: return False return True def refresh_from_db(self, fields=None): """Reload this object from the database, overwriting local field values. :parameters: - `fields`: An iterable of fields to reload. Defaults to all fields. .. warning:: This method will reload the object from the database, possibly with only a subset of fields. Calling :meth:`~pymodm.MongoModel.save` after this may revert or unset fields in the database. """ fields = validate_list_tuple_or_none('fields', fields) if self._qs is None: raise OperationError('Cannot refresh from db before saving.') qs = self._qs.values() if fields: qs = qs.only(*fields) db_inst = qs.first() self._set_attributes(db_inst) return self def __eq__(self, other): if self.pk is not None: if isinstance(other, self.__class__) and other.pk is not None: return self.pk == other.pk elif isinstance(other, DBRef): return self.pk == other.id return self is other