ReferenceFields can now reference abstract Document types
A class that inherits from an abstract Document type is stored in the database as a reference with a 'cls' field that is the class name of the document being stored. Fixes #837
This commit is contained in:
parent
19cbb442ee
commit
013227323d
@ -928,9 +928,14 @@ class ReferenceField(BaseField):
|
|||||||
self._auto_dereference = instance._fields[self.name]._auto_dereference
|
self._auto_dereference = instance._fields[self.name]._auto_dereference
|
||||||
# Dereference DBRefs
|
# Dereference DBRefs
|
||||||
if self._auto_dereference and isinstance(value, DBRef):
|
if self._auto_dereference and isinstance(value, DBRef):
|
||||||
value = self.document_type._get_db().dereference(value)
|
if hasattr(value, 'cls'):
|
||||||
|
# Dereference using the class type specified in the reference
|
||||||
|
cls = get_document(value.cls)
|
||||||
|
else:
|
||||||
|
cls = self.document_type
|
||||||
|
value = cls._get_db().dereference(value)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
instance._data[self.name] = self.document_type._from_son(value)
|
instance._data[self.name] = cls._from_son(value)
|
||||||
|
|
||||||
return super(ReferenceField, self).__get__(instance, owner)
|
return super(ReferenceField, self).__get__(instance, owner)
|
||||||
|
|
||||||
@ -940,22 +945,30 @@ class ReferenceField(BaseField):
|
|||||||
return document.id
|
return document.id
|
||||||
return document
|
return document
|
||||||
|
|
||||||
id_field_name = self.document_type._meta['id_field']
|
|
||||||
id_field = self.document_type._fields[id_field_name]
|
|
||||||
|
|
||||||
if isinstance(document, Document):
|
if isinstance(document, Document):
|
||||||
# We need the id from the saved object to create the DBRef
|
# We need the id from the saved object to create the DBRef
|
||||||
id_ = document.pk
|
id_ = document.pk
|
||||||
if id_ is None:
|
if id_ is None:
|
||||||
self.error('You can only reference documents once they have'
|
self.error('You can only reference documents once they have'
|
||||||
' been saved to the database')
|
' been saved to the database')
|
||||||
|
|
||||||
|
# Use the attributes from the document instance, so that they
|
||||||
|
# override the attributes of this field's document type
|
||||||
|
cls = document
|
||||||
else:
|
else:
|
||||||
id_ = document
|
id_ = document
|
||||||
|
cls = self.document_type
|
||||||
|
|
||||||
|
id_field_name = cls._meta['id_field']
|
||||||
|
id_field = cls._fields[id_field_name]
|
||||||
|
|
||||||
id_ = id_field.to_mongo(id_)
|
id_ = id_field.to_mongo(id_)
|
||||||
if self.dbref:
|
if self.dbref:
|
||||||
collection = self.document_type._get_collection_name()
|
collection = cls._get_collection_name()
|
||||||
return DBRef(collection, id_)
|
return DBRef(collection, id_)
|
||||||
|
elif self.document_type._meta.get('abstract'):
|
||||||
|
collection = cls._get_collection_name()
|
||||||
|
return DBRef(collection, id_, cls=cls._class_name)
|
||||||
|
|
||||||
return id_
|
return id_
|
||||||
|
|
||||||
|
@ -2281,6 +2281,56 @@ class FieldTest(unittest.TestCase):
|
|||||||
Member.drop_collection()
|
Member.drop_collection()
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
def test_reference_class_with_abstract_parent(self):
|
||||||
|
"""Ensure that a class with an abstract parent can be referenced.
|
||||||
|
"""
|
||||||
|
class Sibling(Document):
|
||||||
|
name = StringField()
|
||||||
|
meta = {"abstract": True}
|
||||||
|
|
||||||
|
class Sister(Sibling):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Brother(Sibling):
|
||||||
|
sibling = ReferenceField(Sibling)
|
||||||
|
|
||||||
|
Sister.drop_collection()
|
||||||
|
Brother.drop_collection()
|
||||||
|
|
||||||
|
sister = Sister(name="Alice")
|
||||||
|
sister.save()
|
||||||
|
brother = Brother(name="Bob", sibling=sister)
|
||||||
|
brother.save()
|
||||||
|
|
||||||
|
self.assertEquals(Brother.objects[0].sibling.name, sister.name)
|
||||||
|
|
||||||
|
Sister.drop_collection()
|
||||||
|
Brother.drop_collection()
|
||||||
|
|
||||||
|
def test_reference_abstract_class(self):
|
||||||
|
"""Ensure that an abstract class instance cannot be used in the
|
||||||
|
reference of that abstract class.
|
||||||
|
"""
|
||||||
|
class Sibling(Document):
|
||||||
|
name = StringField()
|
||||||
|
meta = {"abstract": True}
|
||||||
|
|
||||||
|
class Sister(Sibling):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Brother(Sibling):
|
||||||
|
sibling = ReferenceField(Sibling)
|
||||||
|
|
||||||
|
Sister.drop_collection()
|
||||||
|
Brother.drop_collection()
|
||||||
|
|
||||||
|
sister = Sibling(name="Alice")
|
||||||
|
brother = Brother(name="Bob", sibling=sister)
|
||||||
|
self.assertRaises(ValidationError, brother.save)
|
||||||
|
|
||||||
|
Sister.drop_collection()
|
||||||
|
Brother.drop_collection()
|
||||||
|
|
||||||
def test_generic_reference(self):
|
def test_generic_reference(self):
|
||||||
"""Ensure that a GenericReferenceField properly dereferences items.
|
"""Ensure that a GenericReferenceField properly dereferences items.
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user