Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing Bi-Directional relationships in MongoEngine

I'm building a Django application that uses MongoDB and MongoEngine to store data. To present a simplified version of my problem, say I want to have two classes: User and Page. Each page should associate itself with a user and each user a page.

from mongoengine import *

class Page(Document):
    pass

class User(Document):
    name = StringField()
    page = ReferenceField(Page)

class Page(Document):
    content = StringField()
    user = ReferenceField(User)

(Note that Page must be defined before User. If I am missing a Pythonic way to handle circular dependencies, let me know.) Each document can be created and saved just fine, but assigning a Page to a User throws an error.

u = User(name='Jeff')
u.save()
p = Page(content="I'm a page!")
p.save()
p.user = u
p.save()
u.page = p
u.save()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "build\bdist.win32\egg\mongoengine\document.py", line 71, in save
  File "build\bdist.win32\egg\mongoengine\base.py", line 303, in validate
mongoengine.base.ValidationError: Invalid value for field of type "ReferenceField"

Can anyone explain why this exception is being thrown, what I am doing wrong, and how I can avoid it?

like image 213
Drew Reagan Avatar asked Oct 07 '10 20:10

Drew Reagan


2 Answers

This is the proper solution:

from mongoengine import *

class User(Document):
    name = StringField()
    page = ReferenceField('Page')

class Page(Document):
    content = StringField()
    user = ReferenceField(User)

Use single quotes ('Page') to denote classes that have not yet been defined.

like image 88
Drew Reagan Avatar answered Nov 14 '22 03:11

Drew Reagan


Drew's answer is the best way in this case, but I wanted to mention that you can also use a GenereicReferenceField:

from mongoengine import *

class User(Document):
    name = StringField()
    page = GenericReferenceField()

class Page(Document):
    content = StringField()
    user = ReferenceField(User)

But again, for your specific problem, go with the class name in single quotes.

like image 12
askory Avatar answered Nov 14 '22 03:11

askory