23

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?

Drew Reagan
  • 785
  • 6
  • 10
  • An error I can see is that you have defined a field called author and set a field called user. Is the posted code what you actually ran? – Ian Oct 07 '10 at 21:17
  • Yes, although unfortunately that wasn't the source of my problem. Making that mistake would just add a variable to my object but wouldn't affect the MongoEngine save() function. – Drew Reagan Oct 08 '10 at 20:03

2 Answers2

43

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.

Drew Reagan
  • 785
  • 6
  • 10
  • No problems with this solution, yet we can define models in their individual modular model file. This way we might keep up the references using imports and might help up build Class Connection Diagrams using IDE's – Vaishnav Mhetre Jun 18 '18 at 15:19
12

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.

askory
  • 121
  • 1
  • 3