MongoEngine / mongoengine

A Python Object-Document-Mapper for working with MongoDB
http://mongoengine.org
MIT License
4.22k stars 1.23k forks source link

EmbeddedDocument.__eq__ fails if document is unsaved #797

Open z0u opened 9 years ago

z0u commented 9 years ago

An embedded document that contains a ReferenceField can not be compared to another until it is saved.

import mongoengine

class Bar(mongoengine.Document):
    baz = mongoengine.StringField()

class Foo(mongoengine.EmbeddedDocument):
    bar = mongoengine.ReferenceField(Bar)

a = Foo(bar=Bar())
b = Foo(bar=Bar())
a == b

The last line throws this exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "~/.virtualenvs/foo/local/lib/python2.7/site-packages/mongoengine/document.py", line 70, in __eq__
    return self.to_mongo() == other.to_mongo()
  File "~/.virtualenvs/foo/local/lib/python2.7/site-packages/mongoengine/base/document.py", line 255, in to_mongo
    value = field.to_mongo(value)
  File "~/.virtualenvs/foo/local/lib/python2.7/site-packages/mongoengine/fields.py", line 919, in to_mongo
    self.error('You can only reference documents once they have'
  File "~/.virtualenvs/foo/local/lib/python2.7/site-packages/mongoengine/base/fields.py", line 124, in error
    raise ValidationError(message, errors=errors, field_name=field_name)
mongoengine.errors.ValidationError: You can only reference documents once they have been saved to the database

This is in mongoengine version 0.8.7, Python 2.7.6.

aericson commented 9 years ago

Works fine on master, you might also want to create eq

z0u commented 9 years ago

OK, thanks for that. So is it safe to override __eq__ for Document and EmbeddedDocument?

aericson commented 9 years ago

I don't see it a reason why it shouldn't be as long as you implement it correctly. Usually you could check if self.id is not None and self is other for saved objects. For in memory (not saved) you could do whatever makes sense to your document. Make sure your __eq__ works for both cases. I'm basing my response in Django ORM model from which this is based on. Hopefully someone can correct me if I'm wrong.