28

I'm trying to use django, and mongoengine to provide the storage backend only with GridFS. I still have a MySQL database.

I'm running into a strange (to me) error when I'm deleting from the django admin and am wondering if I am doing something incorrectly.

my code looks like this:

# settings.py
from mongoengine import connect
connect("mongo_storage")

# models.py
from mongoengine.django.storage import GridFSStorage
class MyFile(models.Model):
    name = models.CharField(max_length=50)
    content = models.FileField(upload_to="appsfiles", storage=GridFSStorage())
    creation_time = models.DateTimeField(auto_now_add=True)
    last_update_time = models.DateTimeField(auto_now=True)

I am able to upload files just fine, but when I delete them, something seems to break and the mongo database seems to get in an unworkable state until I manually delete all FileDocument.objects. When this happens I can't upload files or delete them from the django interface.

From the stack trace I have:

/home/projects/vector/src/mongoengine/django/storage.py in _get_doc_with_name
        doc = [d for d in docs if getattr(d, self.field).name == name] ...
▼ Local vars
Variable    Value
_[1]    
[]
d   

docs    
Error in formatting: cannot set options after executing query
name    
u'testfile.pdf'
self    

/home/projects/vector/src/mongoengine/fields.py in __getattr__
        raise AttributeError 

Am I using this feature incorrectly?

UPDATE:

thanks to @zeekay's answer I was able to get a working gridfs storage plugin to work. I ended up not using mongoengine at all. I put my adapted solution on github. There is a clear sample project showing how to use it. I also uploaded the project to pypi.

Another Update:

I'd highly recommend the django-storages project. It has lots of storage backed options and is used by many more people than my original proposed solution.

Aaron
  • 4,206
  • 3
  • 24
  • 28
  • I've been hoping to do something like this for the Mayan [link](https://github.com/rosarior/mayan) instalation at work. But I've got no idea how to fix your problem. – Roberto Rosario Mar 03 '11 at 05:44
  • Not quite what you might be looking for, but I added a GridFsStorage backend for Mayan [link](http://goo.gl/7BwkZ). Is very simple and only depends on Pymongo, you could try to use it for your application. – Roberto Rosario Mar 04 '11 at 06:14

1 Answers1

8

I think you are better off not using MongoEngine for this, I haven't had much luck with it either. Here is a drop-in replacement for mongoengine.django.storage.GridFSStorage, which works with the admin.

from django.core.files.storage import Storage
from django.conf import settings

from pymongo import Connection
from gridfs import GridFS

class GridFSStorage(Storage):
    def __init__(self, host='localhost', port=27017, collection='fs'):
        for s in ('host', 'port', 'collection'):
            name = 'GRIDFS_' + s.upper()
            if hasattr(settings, name):
                setattr(self, s, getattr(settings, name))
        for s, v in zip(('host', 'port', 'collection'), (host, port, collection)):
            if v:
                setattr(self, s, v)
        self.db = Connection(host=self.host, port=self.port)[self.collection]
        self.fs = GridFS(self.db)

    def _save(self, name, content):
        self.fs.put(content, filename=name)
        return name

    def _open(self, name, *args, **kwars):
        return self.fs.get_last_version(filename=name)

    def delete(self, name):
        oid = fs.get_last_version(filename=name)._id
        self.fs.delete(oid)

    def exists(self, name):
        return self.fs.exists({'filename': name})

    def size(self, name):
        return self.fs.get_last_version(filename=name).length

GRIDFS_HOST, GRIDFS_PORT and GRIDFS_COLLECTION can be defined in your settings or passed as host, port, collection keyword arguments to GridFSStorage in your model's FileField.

I referred to Django's custom storage documenation, and loosely followed this answer to a similar question.

Community
  • 1
  • 1
Zach Kelling
  • 52,505
  • 13
  • 109
  • 108
  • Great, thanks! So far I haven't noticed any issues with this solution. Do you have a project on Github or anywhere containing this? It seems like it could serve the community well if it was readily findable and accessible. – Aaron Jun 01 '11 at 20:58
  • Nope, I'll try to package it up and stick it on pypi/github! – Zach Kelling Jun 01 '11 at 21:15
  • Hi Zeekay, I really appreciate your work getting this headed in the right direction. I put a project on github that uses a modified version of your storage.py and has a sample app to plug it into a working django app: https://github.com/madisona/django-mongo-storage – Aaron Jun 24 '11 at 15:53
  • @Aaron Nice job :) You should put it on pypi too. Should probably move the example project to an examples subdir, and add a setup.py so it's easy to install. Great guide for packaging here: http://guide.python-distribute.org/. – Zach Kelling Jun 24 '11 at 15:58
  • Or just merge it with mongoengine (which is also on GitHub). I may do that in a few days. – Avery Richardson Jun 30 '11 at 23:36
  • There are some minor issues with this code, I have suggested some changes which should be in queue. For anyone who's interested with the changes: https://gist.github.com/1382323 – cheeming Nov 21 '11 at 15:30