Repository: MongoEngine/mongoengine

Issue:
Inconsistent Behavior of __eq__ for EmbeddedDocuments with LazyReferenceFields
If you try to compare two EmbeddedDocuments that contain a LazyReferenceField, sometimes they are equal and sometimes they are not, depending on what's been done with the EmbeddedDocument. This occurs because the LazyReferenceField stores the ObjectId in the EmbeddedDocument's `_data` field instead of a DBRef, until the field is accessed explicitly, at which point it is replaced with a LazyReference.

A simple test case that shows this is below.

```python
class Job(EmbeddedDocument):
    boss = LazyReferenceField('Person')

class Person(Document):
    job = EmbeddedDocumentField(Job)

Person.drop_collection()

boss = Person()
worker = Person(job=Job(boss=boss))
boss.save()
worker.save()

worker1 = Person.objects.get(id=worker.id)

print(worker1.job == worker.job)
print(worker1.job.boss)
print(worker1.job == worker.job)
```


Discussion:

Resolving patch:
diff --git a/mongoengine/fields.py b/mongoengine/fields.py
--- a/mongoengine/fields.py
+++ b/mongoengine/fields.py
@@ -2502,6 +2502,13 @@ def to_mongo(self, value):
         else:
             return pk
 
+    def to_python(self, value):
+        """Convert a MongoDB-compatible type to a Python type."""
+        if not isinstance(value, (DBRef, Document, EmbeddedDocument)):
+            collection = self.document_type._get_collection_name()
+            value = DBRef(collection, self.document_type.id.to_python(value))
+        return value
+
     def validate(self, value):
         if isinstance(value, LazyReference):
             if value.collection != self.document_type._get_collection_name():


Test patch:
diff --git a/tests/document/test_instance.py b/tests/document/test_instance.py
--- a/tests/document/test_instance.py
+++ b/tests/document/test_instance.py
@@ -3319,6 +3319,39 @@ class Embedded(EmbeddedDocument):
         f1.ref  # Dereferences lazily
         assert f1 == f2
 
+    def test_embedded_document_equality_with_lazy_ref(self):
+        class Job(EmbeddedDocument):
+            boss = LazyReferenceField("Person")
+            boss_dbref = LazyReferenceField("Person", dbref=True)
+
+        class Person(Document):
+            job = EmbeddedDocumentField(Job)
+
+        Person.drop_collection()
+
+        boss = Person()
+        worker = Person(job=Job(boss=boss, boss_dbref=boss))
+        boss.save()
+        worker.save()
+
+        worker1 = Person.objects.get(id=worker.id)
+
+        # worker1.job should be equal to the job used originally to create the
+        # document.
+        self.assertEqual(worker1.job, worker.job)
+
+        # worker1.job should be equal to a newly created Job EmbeddedDocument
+        # using either the Boss object or his ID.
+        self.assertEqual(worker1.job, Job(boss=boss, boss_dbref=boss))
+        self.assertEqual(worker1.job, Job(boss=boss.id, boss_dbref=boss.id))
+
+        # The above equalities should also hold after worker1.job.boss has been
+        # fetch()ed.
+        worker1.job.boss.fetch()
+        self.assertEqual(worker1.job, worker.job)
+        self.assertEqual(worker1.job, Job(boss=boss, boss_dbref=boss))
+        self.assertEqual(worker1.job, Job(boss=boss.id, boss_dbref=boss.id))
+
     def test_dbref_equality(self):
         class Test2(Document):
             name = StringField()