Question

Here is a simple scenario: I have a blog post with comments attach to it. Now I want to use mongoforms to let user submit their comments. Therefore I have this models.py:

class Post(Document):
    title = StringField(max_length=60)
    body = StringField()
    created = DateTimeField(default=datetime.datetime.now)
    comments = ListField(EmbeddedDocumentField('Comment'))

class Comment(EmbeddedDocument):
    author = StringField(max_length=60)
    body = StringField()
    created = DateTimeField(default=datetime.datetime.now)

And I'm using django-mongoforms to support the similar ModelForm functionality, within forms.py:

class CommentForm(MongoForm):
    class Meta:
        document = Comment
        fields = ('author', 'body')

In the views.py, I just want to save what user has entered, more specifically the Author and Body fields of a comment, here is the method to add a comment:

def add_comment(request, id):
    post = Post.objects.with_id(id)
    if request.method == 'POST':
        form = CommentForm(request.POST, instance=post.comments)
        if form.is_valid():
            form.save()
    return HttpResponseRedirect(post.get_absolute_url())

Then once I click Submit button here comes the error message:

instance must be a mongoengine document, not BaseList

So questions are:

  1. How to covert BaseList to Document in MongoEngine? I have looked through the document without a hint.
  2. What's the best practices here generally to handle the EmbeddedDocument? Will ReferenceDocument help?

Also there is a related questions here but without an answer: What is the proper way to update a listfield of embedded documents in mongoengine?

Was it helpful?

Solution

As comments are embedded you'll need to find and update the existing comment in the post object. You can then overwrite the old comment with the new comment (where i is the index) eg:

post.comments[i] = new_comment

then just do a post.save() and mongoengine will convert that to a $set operation.

Alternatively, you could just write the $set directly eg:

Post.objects(pk=post.pk).update(set__comments__i=comment)

Forms.py

I still use the forms.py but I would pass commit=False to save that will return the comment - then append to the post and save the post like so:

def add_comment(request, id):
    post = Post.objects.with_id(id)
    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            comment = form.save(commit=False)
            post.comments.append(comment)
            post.save()
    return HttpResponseRedirect(post.get_absolute_url())
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top