Improvements to Abstract Base Classes
Added test example highlighting what to do to migrate a class from complex (allows inheritance) to simple.
This commit is contained in:
parent
d32dd9ff62
commit
602d7dad00
@ -263,7 +263,7 @@ class DocumentMetaclass(type):
|
|||||||
superclasses[base._class_name] = base
|
superclasses[base._class_name] = base
|
||||||
superclasses.update(base._superclasses)
|
superclasses.update(base._superclasses)
|
||||||
|
|
||||||
if hasattr(base, '_meta'):
|
if hasattr(base, '_meta') and not base._meta.get('abstract'):
|
||||||
# Ensure that the Document class may be subclassed -
|
# Ensure that the Document class may be subclassed -
|
||||||
# inheritance may be disabled to remove dependency on
|
# inheritance may be disabled to remove dependency on
|
||||||
# additional fields _cls and _types
|
# additional fields _cls and _types
|
||||||
@ -280,7 +280,7 @@ class DocumentMetaclass(type):
|
|||||||
|
|
||||||
# Only simple classes - direct subclasses of Document - may set
|
# Only simple classes - direct subclasses of Document - may set
|
||||||
# allow_inheritance to False
|
# allow_inheritance to False
|
||||||
if not simple_class and not meta['allow_inheritance']:
|
if not simple_class and not meta['allow_inheritance'] and not meta['abstract']:
|
||||||
raise ValueError('Only direct subclasses of Document may set '
|
raise ValueError('Only direct subclasses of Document may set '
|
||||||
'"allow_inheritance" to False')
|
'"allow_inheritance" to False')
|
||||||
attrs['_meta'] = meta
|
attrs['_meta'] = meta
|
||||||
@ -360,8 +360,9 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
|||||||
|
|
||||||
# Subclassed documents inherit collection from superclass
|
# Subclassed documents inherit collection from superclass
|
||||||
for base in bases:
|
for base in bases:
|
||||||
if hasattr(base, '_meta') and 'collection' in base._meta:
|
if hasattr(base, '_meta'):
|
||||||
collection = base._meta['collection']
|
if 'collection' in base._meta:
|
||||||
|
collection = base._meta['collection']
|
||||||
|
|
||||||
# Propagate index options.
|
# Propagate index options.
|
||||||
for key in ('index_background', 'index_drop_dups', 'index_opts'):
|
for key in ('index_background', 'index_drop_dups', 'index_opts'):
|
||||||
@ -370,6 +371,9 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
|||||||
|
|
||||||
id_field = id_field or base._meta.get('id_field')
|
id_field = id_field or base._meta.get('id_field')
|
||||||
base_indexes += base._meta.get('indexes', [])
|
base_indexes += base._meta.get('indexes', [])
|
||||||
|
# Propagate 'allow_inheritance'
|
||||||
|
if 'allow_inheritance' in base._meta:
|
||||||
|
base_meta['allow_inheritance'] = base._meta['allow_inheritance']
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
@ -384,6 +388,7 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
|||||||
'index_opts': {},
|
'index_opts': {},
|
||||||
'queryset_class': QuerySet,
|
'queryset_class': QuerySet,
|
||||||
'delete_rules': {},
|
'delete_rules': {},
|
||||||
|
'allow_inheritance': True
|
||||||
}
|
}
|
||||||
meta.update(base_meta)
|
meta.update(base_meta)
|
||||||
|
|
||||||
|
@ -151,12 +151,12 @@ class DocumentTest(unittest.TestCase):
|
|||||||
"""Ensure that inheritance may be disabled on simple classes and that
|
"""Ensure that inheritance may be disabled on simple classes and that
|
||||||
_cls and _types will not be used.
|
_cls and _types will not be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Animal(Document):
|
class Animal(Document):
|
||||||
meta = {'allow_inheritance': False}
|
|
||||||
name = StringField()
|
name = StringField()
|
||||||
|
meta = {'allow_inheritance': False}
|
||||||
|
|
||||||
Animal.drop_collection()
|
Animal.drop_collection()
|
||||||
|
|
||||||
def create_dog_class():
|
def create_dog_class():
|
||||||
class Dog(Animal):
|
class Dog(Animal):
|
||||||
pass
|
pass
|
||||||
@ -191,6 +191,92 @@ class DocumentTest(unittest.TestCase):
|
|||||||
self.assertFalse('_cls' in comment.to_mongo())
|
self.assertFalse('_cls' in comment.to_mongo())
|
||||||
self.assertFalse('_types' in comment.to_mongo())
|
self.assertFalse('_types' in comment.to_mongo())
|
||||||
|
|
||||||
|
def test_allow_inheritance_abstract_document(self):
|
||||||
|
"""Ensure that abstract documents can set inheritance rules and that
|
||||||
|
_cls and _types will not be used.
|
||||||
|
"""
|
||||||
|
class FinalDocument(Document):
|
||||||
|
meta = {'abstract': True,
|
||||||
|
'allow_inheritance': False}
|
||||||
|
|
||||||
|
class Animal(FinalDocument):
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
Animal.drop_collection()
|
||||||
|
def create_dog_class():
|
||||||
|
class Dog(Animal):
|
||||||
|
pass
|
||||||
|
self.assertRaises(ValueError, create_dog_class)
|
||||||
|
|
||||||
|
# Check that _cls etc aren't present on simple documents
|
||||||
|
dog = Animal(name='dog')
|
||||||
|
dog.save()
|
||||||
|
collection = self.db[Animal._meta['collection']]
|
||||||
|
obj = collection.find_one()
|
||||||
|
self.assertFalse('_cls' in obj)
|
||||||
|
self.assertFalse('_types' in obj)
|
||||||
|
|
||||||
|
Animal.drop_collection()
|
||||||
|
|
||||||
|
def test_how_to_turn_off_inheritance(self):
|
||||||
|
"""Demonstrates migrating from allow_inheritance = True to False.
|
||||||
|
"""
|
||||||
|
class Animal(Document):
|
||||||
|
name = StringField()
|
||||||
|
meta = {
|
||||||
|
'indexes': ['name']
|
||||||
|
}
|
||||||
|
|
||||||
|
Animal.drop_collection()
|
||||||
|
|
||||||
|
dog = Animal(name='dog')
|
||||||
|
dog.save()
|
||||||
|
|
||||||
|
collection = self.db[Animal._meta['collection']]
|
||||||
|
obj = collection.find_one()
|
||||||
|
self.assertTrue('_cls' in obj)
|
||||||
|
self.assertTrue('_types' in obj)
|
||||||
|
|
||||||
|
info = collection.index_information()
|
||||||
|
info = [value['key'] for key, value in info.iteritems()]
|
||||||
|
self.assertEquals([[(u'_id', 1)], [(u'_types', 1)], [(u'_types', 1), (u'name', 1)]], info)
|
||||||
|
|
||||||
|
# Turn off inheritance
|
||||||
|
class Animal(Document):
|
||||||
|
name = StringField()
|
||||||
|
meta = {
|
||||||
|
'allow_inheritance': False,
|
||||||
|
'indexes': ['name']
|
||||||
|
}
|
||||||
|
collection.update({}, {"$unset": {"_types": 1, "_cls": 1}}, False, True)
|
||||||
|
|
||||||
|
# Confirm extra data is removed
|
||||||
|
obj = collection.find_one()
|
||||||
|
self.assertFalse('_cls' in obj)
|
||||||
|
self.assertFalse('_types' in obj)
|
||||||
|
|
||||||
|
info = collection.index_information()
|
||||||
|
info = [value['key'] for key, value in info.iteritems()]
|
||||||
|
self.assertEquals([[(u'_id', 1)], [(u'_types', 1)], [(u'_types', 1), (u'name', 1)]], info)
|
||||||
|
|
||||||
|
info = collection.index_information()
|
||||||
|
indexes_to_drop = [key for key, value in info.iteritems() if '_types' in dict(value['key'])]
|
||||||
|
for index in indexes_to_drop:
|
||||||
|
collection.drop_index(index)
|
||||||
|
|
||||||
|
info = collection.index_information()
|
||||||
|
info = [value['key'] for key, value in info.iteritems()]
|
||||||
|
self.assertEquals([[(u'_id', 1)]], info)
|
||||||
|
|
||||||
|
# Recreate indexes
|
||||||
|
dog = Animal.objects.first()
|
||||||
|
dog.save()
|
||||||
|
info = collection.index_information()
|
||||||
|
info = [value['key'] for key, value in info.iteritems()]
|
||||||
|
self.assertEquals([[(u'_id', 1)], [(u'name', 1),]], info)
|
||||||
|
|
||||||
|
Animal.drop_collection()
|
||||||
|
|
||||||
def test_abstract_documents(self):
|
def test_abstract_documents(self):
|
||||||
"""Ensure that a document superclass can be marked as abstract
|
"""Ensure that a document superclass can be marked as abstract
|
||||||
thereby not using it as the name for the collection."""
|
thereby not using it as the name for the collection."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user