A simple CRUD app with Django and Mongoengine

There are several possibilities to use MongoDB in Django:

Django MongoDB uses django-nonrel, which is a fork based on Django 1.3.
I don’t like this idea, because now Django 1.5 is ready to out. Beetween Mongoengine and MongoKit, I like more Mongoengine. There are several comparative articles:

So I created a simple CRUD app using Mongoengine.

The model definition in Mongoengine looks like in Django.

class Post(Document):
    user = ReferenceField(User, reverse_delete_rule=CASCADE)
    title = StringField(max_length=200, required=True)
    text = StringField(required=True)
    text_length = IntField()
    date_modified = DateTimeField(default=datetime.now)
    is_published = BooleanField()
    tags = ListField(ReferenceField(Tag))

    def __unicode__(self):
        return self.title

    def save(self, *args, **kwargs):
        self.text_length = len(self.text)
        return super(Post, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('post-detail', args=[self.id])

You can find the full fields list in the documentation.

The default TEST_RUNNER throws error: django.core.exceptions.ImproperlyConfigured: settings.DATABASES is improperly configured. Please supply the ENGINE value. Check settings documentation for more details. I used MongoTestSuiteRunner and MongoTestCase from https://github.com/lig/mongoengine/blob/django-testing-utils/mongoengine/django/tests.py.

There are several problems with using of generic views.

  • Mongoengine document has not _meta.app_label, _meta.object_name properties
  • There is no ModelForm for create, update views

It can be solved by using django-mongogeneric and django-mongodbforms.

In my app I’ve just overrided get_template_names, get_query_set, get_object methods and set context_object_name property.

class TagListView(ListView):
    model = Tag
    context_object_name = "tag_list"

    def get_template_names(self):
        return ["blog/tag_list.html"]

    def get_queryset(self):
        return Tag.objects

And emulated ModelForm with simple Form.

forms.py
class PostForm(forms.Form):
    title = forms.CharField(max_length=255)
    text = forms.CharField(widget=forms.widgets.Textarea())
    is_published = forms.BooleanField(required=False)
    tags = forms.MultipleChoiceField(
        widget=forms.widgets.CheckboxSelectMultiple(), 
        required=False)

    def __init__(self, *args, **kwargs):
        self.instance = kwargs.pop('instance', None)
        super(PostForm, self).__init__(*args, **kwargs)
        self.fields['tags'].choices = [(tag.id, tag.title) for tag in Tag.objects]
        if self.instance:
            self.fields['title'].initial = self.instance.title
            self.fields['text'].initial = self.instance.text
            self.fields['is_published'].initial = self.instance.is_published        
            self.fields['tags'].initial = [tag.id for tag in self.instance.tags]


    def save(self, commit=True):
        post = self.instance if self.instance else Post()
        post.title = self.cleaned_data['title']
        post.text = self.cleaned_data['text']
        post.is_published = self.cleaned_data['is_published']
        post.tags = Tag.objects(id__in=self.cleaned_data['tags'])
        if commit:
            post.save()

        return post

views.py:
class PostCreateView(CreateView):
    model = Post
    form_class = PostForm       
...
comments powered by Disqus