Document Inheritance
MongoEngine supports two distinct inheritance mechanisms that solve different problems. Polymorphic inheritance (allow_inheritance = True) lets subclasses of a document store in the same collection and be queried through the base class - an Image and a Video are both Media. Abstract inheritance (abstract = True) shares field definitions across unrelated documents without any polymorphism - a base class that contributes created_at and updated_at to everything. Knowing which one a situation calls for is the whole chapter.
Polymorphic Inheritance
from mongoengine import Document, StringField, FloatField
class Media(Document):
meta = {"allow_inheritance": True}
title = StringField(required=True)
uploaded_at = DateTimeField(default=datetime.datetime.now(datetime.timezone.utc))
class Image(Media):
url = StringField(required=True)
width = IntField()
height = IntField()
class Video(Media):
url = StringField(required=True)
duration = FloatField()
img = Image(title="sunset", url="https://x/1.jpg", width=800, height=600)
vid = Video(title="talk", url="https://x/2.mp4", duration=95.5)
img.save(); vid.save()
With allow_inheritance, each stored document carries a _t field recording its concrete class. The queries that follow are the behavior that matters:
Image.objects.count() # only images - the _t discriminator filters
Video.objects.count() # only videos
Media.objects.count() # BOTH - the base class sees every subclass
media = Media.objects.first(). # may be an Image or a Video instance
print(type(media))
The base-class queryset is polymorphic: Media.objects returns whatever concrete type each document is, and subclass querysets are automatically restricted to their own _t. The trade is a small per-document overhead (the _t key) and the fact that queries through the base class can be slower than queries on a single-type collection - the discriminator prevents some index optimizations. Use it when the types genuinely share an identity and are queried together ("all media, whatever kind"), not as a general-purpose base class.
Abstract Base Classes
from mongoengine import Document, DateTimeField, StringField
class TimeStamped(Document):
meta = {"abstract": True}
created_at = DateTimeField(default=datetime.datetime.now(datetime.timezone.utc))
updated_at = DateTimeField(default=datetime.datetime.now(datetime.timezone.utc))
class Order(TimeStamped):
sku = StringField()
class Invoice(TimeStamped):
number = StringField()
Order.objects.count() # orders only - no _t, separate collections, no magic
An abstract = True class is a template, not a document type: it has no collection of its own, cannot be instantiated or queried, and its subclasses are ordinary documents in their own collections. Field definitions, defaults, and methods are inherited; there is no discriminator and no polymorphic querying. This is the right tool for "every document in this app gets timestamps and audit fields", which is most of what people reach for when they think they want document inheritance.
Choosing Between Them
| Need | Use |
|---|---|
| Shared fields/methods across unrelated documents | abstract base class |
| Subtypes stored together, queried via the base | polymorphic (allow_inheritance) |
| Subtypes that are really separate concerns | separate documents, no inheritance |
The default answer is "no inheritance" - separate document classes with an abstract base for shared fields. Polymorphic inheritance earns its complexity only when the base-class queryset is a real, frequent query and the subtypes are closed and known. It is a modeling decision with query-performance consequences, not a free language feature.
Limits
- Only single-level inheritance from an
allow_inheritancedocument: a subclass of a subclass is not itself queryable through the grandparent in the usual way - keep the hierarchy one level deep. - Fields must not conflict: a subclass cannot redeclare a field with a different type than the base.
- All classes in the hierarchy must be importable when documents load - the
_tvalue is a dotted class path, so renaming or moving a model class orphans its documents.
Summary
Use an abstract = True base class to share fields and methods across unrelated documents - no collections, no discriminator, no cost. Use allow_inheritance = True only when subtypes belong in one collection and the base-class queryset is a real query pattern: the _t discriminator makes Base.objects polymorphic and Sub.objects self-restricting. Keep hierarchies one level, never rename a polymorphic class casually, and reach for separate documents when the "subtypes" do not share an identity.