Ron
Ron

Reputation: 23516

MongoDB - MongoEngine - Saving an embedded document doesn't work - Has no attribute save

I have an embedded document class Post and a father-class Thread.

class Thread(Document):
    ...
    posts = ListField(EmbeddedDocumentField("Post"))

class Post(EmbeddedDocument): 
    attribute = StringField()
    ...

I want to create a new post and add it to my ListField in the Thread class.

My code looks like this:

post = Post()
post.attribute = "noodle"
post.save()
thread.posts.append(post)
thread.save()

But I get the following error message:

"'Post' object has no attribute 'save'"

If I skip the post.save() an empty Post object is appended to my Thread.

Any ideas?

Upvotes: 3

Views: 10556

Answers (2)

Ross
Ross

Reputation: 18111

Your code looks fine - are you sure you don't have other thread objects? Heres a test case proving your code (without the post.save() step). What version do you have installed?

import unittest
from mongoengine import *


class Test(unittest.TestCase):

    def setUp(self):
        conn = connect(db='mongoenginetest')

    def test_something(self):

        class Thread(Document):
            posts = ListField(EmbeddedDocumentField("Post"))

        class Post(EmbeddedDocument):
            attribute = StringField()

        Thread.drop_collection()

        thread = Thread()
        post = Post()
        post.attribute = "Hello"

        thread.posts.append(post)
        thread.save()

        thread = Thread.objects.first()
        self.assertEqual(1, len(thread.posts))
        self.assertEqual("Hello", thread.posts[0].attribute)

Upvotes: 5

Filip Dupanović
Filip Dupanović

Reputation: 33700

Embedded documents do not exist as individual, separate instances from their document instance, i.e. to save an embedded document you have to save the document itself where it's embedded into; another way to look at it is that you cannot store an embedded document without an actual document.

This is also the reason that, while you can filter documents that contain a particular embedded document, you will not receive the matching embedded document itself--you'll receive the whole document it's a part of.

thread = Thread.objects.first()  # Get the thread
post = Post()
post.attribute = "noodle"
thread.posts.append(post)  # Append the post
thread.save()  # The post is now stored as a part of the thread

Upvotes: 8

Related Questions