Added Abstract Base Classes
Thanks to @theojulienne for the code :) #108
This commit is contained in:
		| @@ -253,7 +253,16 @@ class TopLevelDocumentMetaclass(DocumentMetaclass): | |||||||
|         # __metaclass__ is only set on the class with the __metaclass__ |         # __metaclass__ is only set on the class with the __metaclass__ | ||||||
|         # attribute (i.e. it is not set on subclasses). This differentiates |         # attribute (i.e. it is not set on subclasses). This differentiates | ||||||
|         # 'real' documents from the 'Document' class |         # 'real' documents from the 'Document' class | ||||||
|         if attrs.get('__metaclass__') == TopLevelDocumentMetaclass: |         # | ||||||
|  |         # Also assume a class is abstract if it has abstract set to True in | ||||||
|  |         # its meta dictionary. This allows custom Document superclasses. | ||||||
|  |         if (attrs.get('__metaclass__') == TopLevelDocumentMetaclass or | ||||||
|  |             ('meta' in attrs and attrs['meta'].get('abstract', False))): | ||||||
|  |             # Make sure no base class was non-abstract | ||||||
|  |             non_abstract_bases = [b for b in bases | ||||||
|  |                 if hasattr(b,'_meta') and not b._meta.get('abstract', False)] | ||||||
|  |             if non_abstract_bases: | ||||||
|  |                 raise ValueError("Abstract document cannot have non-abstract base") | ||||||
|             return super_new(cls, name, bases, attrs) |             return super_new(cls, name, bases, attrs) | ||||||
|  |  | ||||||
|         collection = name.lower() |         collection = name.lower() | ||||||
| @@ -276,6 +285,7 @@ class TopLevelDocumentMetaclass(DocumentMetaclass): | |||||||
|                 base_indexes += base._meta.get('indexes', []) |                 base_indexes += base._meta.get('indexes', []) | ||||||
|  |  | ||||||
|         meta = { |         meta = { | ||||||
|  |             'abstract': False, | ||||||
|             'collection': collection, |             'collection': collection, | ||||||
|             'max_documents': None, |             'max_documents': None, | ||||||
|             'max_size': None, |             'max_size': None, | ||||||
|   | |||||||
| @@ -29,6 +29,9 @@ class DocumentTest(unittest.TestCase): | |||||||
|             age = IntField() |             age = IntField() | ||||||
|         self.Person = Person |         self.Person = Person | ||||||
|  |  | ||||||
|  |     def tearDown(self): | ||||||
|  |         self.Person.drop_collection() | ||||||
|  |  | ||||||
|     def test_drop_collection(self): |     def test_drop_collection(self): | ||||||
|         """Ensure that the collection may be dropped from the database. |         """Ensure that the collection may be dropped from the database. | ||||||
|         """ |         """ | ||||||
| @@ -188,6 +191,34 @@ 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_abstract_documents(self): | ||||||
|  |         """Ensure that a document superclass can be marked as abstract | ||||||
|  |         thereby not using it as the name for the collection.""" | ||||||
|  |  | ||||||
|  |         class Animal(Document): | ||||||
|  |             name = StringField() | ||||||
|  |             meta = {'abstract': True} | ||||||
|  |  | ||||||
|  |         class Fish(Animal): pass | ||||||
|  |         class Guppy(Fish): pass | ||||||
|  |  | ||||||
|  |         class Mammal(Animal): | ||||||
|  |             meta = {'abstract': True} | ||||||
|  |         class Human(Mammal): pass | ||||||
|  |  | ||||||
|  |         self.assertFalse('collection' in Animal._meta) | ||||||
|  |         self.assertFalse('collection' in Mammal._meta) | ||||||
|  |  | ||||||
|  |         self.assertEqual(Fish._meta['collection'], 'fish') | ||||||
|  |         self.assertEqual(Guppy._meta['collection'], 'fish') | ||||||
|  |         self.assertEqual(Human._meta['collection'], 'human') | ||||||
|  |  | ||||||
|  |         def create_bad_abstract(): | ||||||
|  |             class EvilHuman(Human): | ||||||
|  |                 evil = BooleanField(default=True) | ||||||
|  |                 meta = {'abstract': True} | ||||||
|  |         self.assertRaises(ValueError, create_bad_abstract) | ||||||
|  |  | ||||||
|     def test_collection_name(self): |     def test_collection_name(self): | ||||||
|         """Ensure that a collection with a specified name may be used. |         """Ensure that a collection with a specified name may be used. | ||||||
|         """ |         """ | ||||||
| @@ -907,9 +938,6 @@ class DocumentTest(unittest.TestCase): | |||||||
|         A.drop_collection() |         A.drop_collection() | ||||||
|         B.drop_collection() |         B.drop_collection() | ||||||
|  |  | ||||||
|     def tearDown(self): |  | ||||||
|         self.Person.drop_collection() |  | ||||||
|  |  | ||||||
|     def test_document_hash(self): |     def test_document_hash(self): | ||||||
|         """Test document in list, dict, set |         """Test document in list, dict, set | ||||||
|         """ |         """ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user