diff --git a/docs/django.rst b/docs/django.rst index 2cce3f02..8a490571 100644 --- a/docs/django.rst +++ b/docs/django.rst @@ -47,9 +47,10 @@ into you settings module:: Storage ======= -With MongoEngine's support for GridFS via the FileField, it is useful to have a -Django file storage backend that wraps this. The new storage module is called -GridFSStorage. Using it is very similar to using the default FileSystemStorage.:: +With MongoEngine's support for GridFS via the :class:`~mongoengine.FileField`, +it is useful to have a Django file storage backend that wraps this. The new +storage module is called :class:`~mongoengine.django.GridFSStorage`. Using it +is very similar to using the default FileSystemStorage.:: fs = mongoengine.django.GridFSStorage() @@ -57,29 +58,30 @@ GridFSStorage. Using it is very similar to using the default FileSystemStorage.: All of the `Django Storage API methods `_ have been -implemented except ``path()``. If the filename provided already exists, an +implemented except :func:`path`. If the filename provided already exists, an underscore and a number (before # the file extension, if one exists) will be appended to the filename until the generated filename doesn't exist. The -``save()`` method will return the new filename.:: +:func:`save` method will return the new filename.:: - > fs.exists('hello.txt') + >>> fs.exists('hello.txt') True - > fs.open('hello.txt').read() + >>> fs.open('hello.txt').read() 'Hello, World!' - > fs.size('hello.txt') + >>> fs.size('hello.txt') 13 - > fs.url('hello.txt') + >>> fs.url('hello.txt') 'http://your_media_url/hello.txt' - > fs.open('hello.txt').name + >>> fs.open('hello.txt').name 'hello.txt' - > fs.listdir() + >>> fs.listdir() ([], [u'hello.txt']) -All files will be saved and retrieved in GridFS via the ``FileDocument`` document, -allowing easy access to the files without the GridFSStorage backend.:: +All files will be saved and retrieved in GridFS via the :class::`FileDocument` +document, allowing easy access to the files without the GridFSStorage +backend.:: - > from mongoengine.django.storage import FileDocument - > FileDocument.objects() + >>> from mongoengine.django.storage import FileDocument + >>> FileDocument.objects() [] .. versionadded:: 0.4 diff --git a/docs/guide/gridfs.rst b/docs/guide/gridfs.rst index 7c6e8acd..503610c9 100644 --- a/docs/guide/gridfs.rst +++ b/docs/guide/gridfs.rst @@ -1,11 +1,17 @@ ====== GridFS ====== + +.. versionadded:: 0.4 + +Writing +------- + GridFS support comes in the form of the :class:`~mongoengine.FileField` field object. This field acts as a file-like object and provides a couple of -different ways of inserting and retrieving data. Metadata such as content-type -can also be stored alongside the stored files. In the following example, an -document is created to store details about animals, including a photo: +different ways of inserting and retrieving data. Arbitrary metadata such as +content type can also be stored alongside the files. In the following example, +a document is created to store details about animals, including a photo:: class Animal(Document): genus = StringField() @@ -14,13 +20,64 @@ document is created to store details about animals, including a photo: marmot = Animal('Marmota', 'Sciuridae') - marmot_photo = open('marmot.jpg') # Retrieve a photo from disk - marmot.photo = marmot_photo # Store the photo in the document + marmot_photo = open('marmot.jpg', 'r') # Retrieve a photo from disk + marmot.photo = marmot_photo # Store the photo in the document + marmot.photo.content_type = 'image/jpeg' # Store metadata marmot.save() -So adding file data to a document is as easy as adding data to any other +Another way of writing to a :class:`~mongoengine.FileField` is to use the +:func:`put` method. This allows for metadata to be stored in the same call as +the file:: -.. versionadded:: 0.4 + marmot.photo.put(marmot_photo, content_type='image/jpeg') + + marmot.save() + +Retrieval +--------- + +So using the :class:`~mongoengine.FileField` is just like using any other +field. The file can also be retrieved just as easily:: + + marmot = Animal.objects('Marmota').first() + photo = marmot.photo.read() + content_type = marmot.photo.content_type + +Streaming +--------- + +Streaming data into a :class:`~mongoengine.FileField` is achieved in a +slightly different manner. First, a new file must be created by calling the +:func:`new_file` method. Data can then be written using :func:`write`:: + + marmot.photo.new_file() + marmot.photo.write('some_image_data') + marmot.photo.write('some_more_image_data') + marmot.photo.close() + + marmot.photo.save() + +Deletion +-------- + +Deleting stored files is achieved with the :func:`delete` method:: + + marmot.photo.delete() + +.. note:: + The FileField in a Document actually only stores the ID of a file in a + separate GridFS collection. This means that deleting a document + with a defined FileField does not actually delete the file. You must be + careful to delete any files in a Document as above before deleting the + Document itself. +Replacing files +--------------- + +Files can be replaced with the :func:`replace` method. This works just like +the :func:`put` method so even metadata can (and should) be replaced:: + + another_marmot = open('another_marmot.png', 'r') + marmot.photo.replace(another_marmot, content_type='image/png') diff --git a/docs/guide/querying.rst b/docs/guide/querying.rst index 58bf9f63..832fed50 100644 --- a/docs/guide/querying.rst +++ b/docs/guide/querying.rst @@ -34,7 +34,7 @@ arguments. The keys in the keyword arguments correspond to fields on the Fields on embedded documents may also be referred to using field lookup syntax by using a double-underscore in place of the dot in object attribute access syntax:: - + # This will return a QuerySet that will only iterate over pages that have # been written by a user whose 'country' field is set to 'uk' uk_pages = Page.objects(author__country='uk') @@ -67,7 +67,7 @@ Query operators =============== Operators other than equality may also be used in queries; just attach the operator name to a key with a double-underscore:: - + # Only find users whose age is 18 or less young_users = Users.objects(age__lte=18) @@ -144,7 +144,7 @@ You may also index the query to retrieve a single result. If an item at that index does not exists, an :class:`IndexError` will be raised. A shortcut for retrieving the first result and returning :attr:`None` if no result exists is provided (:meth:`~mongoengine.queryset.QuerySet.first`):: - + >>> # Make sure there are no users >>> User.drop_collection() >>> User.objects[0] @@ -458,7 +458,7 @@ that you may use with these methods: The syntax for atomic updates is similar to the querying syntax, but the modifier comes before the field, not after it:: - + >>> post = BlogPost(title='Test', page_views=0, tags=['database']) >>> post.save() >>> BlogPost.objects(id=post.id).update_one(inc__page_views=1) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 30a508d1..ef9540fc 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -586,14 +586,14 @@ class GridFSProxy(object): def put(self, file, **kwargs): if self.grid_id: - raise GridFSError('This document alreay has a file. Either delete ' + raise GridFSError('This document already has a file. Either delete ' 'it or call replace to overwrite it') self.grid_id = self.fs.put(file, **kwargs) def write(self, string): if self.grid_id: if not self.newfile: - raise GridFSError('This document alreay has a file. Either ' + raise GridFSError('This document already has a file. Either ' 'delete it or call replace to overwrite it') else: self.new_file() @@ -622,6 +622,7 @@ class GridFSProxy(object): def replace(self, file, **kwargs): self.delete() + self.grid_id = None self.put(file, **kwargs) def close(self):