Repository: MongoEngine/mongoengine

Issue:
Error when referencing the abstract class in ReferenceField
When you reference an abstract class in ReferenceField, you can save the object but if you then fetch it from the database, modify it and save it, it raises a raises a ValidationError (from .validate). See below

```
class AbstractHuman(Document):
    meta = {'abstract': True}

class Dad(AbstractHuman):
    name = StringField()

class Home(Document):
    dad = ReferenceField(AbstractHuman)  # Referencing the abstract class
    address = StringField()

dad = Dad(name='5').save()
Home(dad=dad, address='street').save()

home = Home.objects.first()
home.address = 'garbage'
home.save()   # raises mongoengine.errors.ValidationError: ValidationError (Home:5bc4f0894ec5dc2abc2b4284) (DBRef(u'dad', ObjectId('5bc4f0894ec5dc2abc2b4283'), cls=u'Dad') is not an instance of abstract reference type Human: ['dad'])
```
I believe this relates to #997. I will submit a PR shortly with a fix
mongoengine.errors.ValidationError: A ReferenceField only accepts DBRef or documents
python 2.7.8, latest mongoengine.zip from master:
mongoengine/fields.py, line 990

``` python
def validate(self, value):
        if not isinstance(value, (self.document_type, DBRef)):
            self.error("A ReferenceField only accepts DBRef or documents")

        if isinstance(value, Document) and value.id is None:
            self.error('You can only reference documents once they have been '
                       'saved to the database')
```

**first "if" gives the following code**

``` python
    s = Shift.objects.get(id='554ad0c18a5da50025b7c7c3')
    print s.guard.fname
    #guard = Guard.objects.get(id='55355d7083ba88e23f8b4569')
    guard = Guard.objects.get(id='54d4361183ba88c24a8b4567')
    print guard.fullname
    s.guard = guard
    s.save()

```

mongoengine.errors.ValidationError: A ReferenceField only accepts DBRef or documents

traceback:
https://gist.github.com/saritasa/27d82f33e3cc33afe1cb

If I run it again - everything works. If I uncomment the first guard and comment 2nd guard, I get this error again. Run 4th time - and error gone. 

value is ObjectId type, when I debug code.

Sample data:

``` json
{
    "_id" : ObjectId("554ad0c18a5da50025b7c7c3"),
    "parent" : ObjectId("55241c448a5da5002616ede5"),
    "type" : "reassignment",
    "date" : ISODate("2015-05-15T00:00:00.000Z"),
    "school" : ObjectId("55241a8983ba889a678b4567"),
    "location" : ObjectId("55241ae583ba88d46c8b4567"),
    "guard" : ObjectId("54d4361183ba88c24a8b4567"),
    "name" : "1st shift",
    "weekday" : 5,
    "start" : "08:00:00",
    "finish" : "08:30:00",
    "start_sec" : 28800,
    "finish_sec" : 30600,
    "weight" : 28800,
    "holidays" : [],
    "created_at" : ISODate("2015-05-07T02:41:05.618Z"),
    "updated_at" : ISODate("2015-05-11T10:01:58.139Z"),
    "guard_name" : "Guard2Vadik alt Kuz",
    "school_name" : "VictorHigh",
    "location_name" : "Joslin / Gard",
    "guard_phone" : "1231231234",
    "parent_cache" : {
        "_id" : ObjectId("55241c448a5da5002616ede5"),
        "finish" : "08:30:00",
        "weight" : 28800,
        "start_sec" : 28800,
        "finish_sec" : 30600,
        "start" : "08:00:00",
        "guard" : ObjectId("552411f883ba8842658b4567"),
        "type" : "regular"
    }
}
```

Full shift class is here:
https://gist.github.com/saritasa/ad9387a1259141db4a04

**any ideas?**



Discussion:

Not really, but this doesn't sound good... There is quite a bit to do here, but if I have time, I may inspect that further. Else, feel free to submit a PR, at least with a proper (failing) test, even better with the fix :-)

I had a similar problem and apparently it occurs when the `ReferenceField`'s document class is abstract.

In my case, inside the `ReferenceField.validate` method, `type(value)` was `myapp.mymodule.documents.subclassmodule.FooBar` while `self.document_type` was `myapp.mymodule.documents..Bar` (there were actually two dots between `documents` and `Bar`).

`FooBar` is a subclass of `Bar` (more precisely, it was declared as `FooBar(FooBarSerializer, Bar):`), but `isinstance(value, self.document_type)` evaluated to `False` (so did `issubclass(type(value), self.document_type)` although I believe that's exactly equivalent).

Hope this helps.

try using
s.guard = guard..to_dbref()
    s.save()
@rahulrameshan you are right, thank you.

Resolving patch:
diff --git a/mongoengine/fields.py b/mongoengine/fields.py
--- a/mongoengine/fields.py
+++ b/mongoengine/fields.py
@@ -1176,15 +1176,6 @@ def validate(self, value):
             self.error('You can only reference documents once they have been '
                        'saved to the database')
 
-        if (
-            self.document_type._meta.get('abstract') and
-            not isinstance(value, self.document_type)
-        ):
-            self.error(
-                '%s is not an instance of abstract reference type %s' % (
-                    value, self.document_type._class_name)
-            )
-
     def lookup_member(self, member_name):
         return self.document_type._fields.get(member_name)
 


Test patch:
diff --git a/tests/document/inheritance.py b/tests/document/inheritance.py
--- a/tests/document/inheritance.py
+++ b/tests/document/inheritance.py
@@ -2,14 +2,11 @@
 import unittest
 import warnings
 
-from datetime import datetime
-
 from tests.fixtures import Base
 
-from mongoengine import Document, EmbeddedDocument, connect
+from mongoengine import Document, EmbeddedDocument, connect, ReferenceField,\
+    BooleanField, GenericReferenceField, IntField, StringField
 from mongoengine.connection import get_db
-from mongoengine.fields import (BooleanField, GenericReferenceField,
-                                IntField, StringField)
 
 __all__ = ('InheritanceTest', )
 
@@ -300,6 +297,41 @@ class Mammal(Animal):
         doc = Animal(name='dog')
         self.assertNotIn('_cls', doc.to_mongo())
 
+    def test_using_abstract_class_in_reference_field(self):
+        # Ensures no regression of #1920
+        class AbstractHuman(Document):
+            meta = {'abstract': True}
+
+        class Dad(AbstractHuman):
+            name = StringField()
+
+        class Home(Document):
+            dad = ReferenceField(AbstractHuman)  # Referencing the abstract class
+            address = StringField()
+
+        dad = Dad(name='5').save()
+        Home(dad=dad, address='street').save()
+
+        home = Home.objects.first()
+        home.address = 'garbage'
+        home.save()     # Was failing with ValidationError
+
+    def test_abstract_class_referencing_self(self):
+        # Ensures no regression of #1920
+        class Human(Document):
+            meta = {'abstract': True}
+            creator = ReferenceField('self', dbref=True)
+
+        class User(Human):
+            name = StringField()
+
+        user = User(name='John').save()
+        user2 = User(name='Foo', creator=user).save()
+
+        user2 = User.objects.with_id(user2.id)
+        user2.name = 'Bar'
+        user2.save()    # Was failing with ValidationError
+
     def test_abstract_handle_ids_in_metaclass_properly(self):
 
         class City(Document):
@@ -358,11 +390,11 @@ class City(Document):
             meta = {'abstract': True,
                     'allow_inheritance': False}
 
-        bkk = City(continent='asia')
-        self.assertEqual(None, bkk.pk)
+        city = City(continent='asia')
+        self.assertEqual(None, city.pk)
         # TODO: expected error? Shouldn't we create a new error type?
         with self.assertRaises(KeyError):
-            setattr(bkk, 'pk', 1)
+            setattr(city, 'pk', 1)
 
     def test_allow_inheritance_embedded_document(self):
         """Ensure embedded documents respect inheritance."""