Polymorphic Associations
Polymorphic associations let one child table reference any of several parent tables through a type column plus an id, sacrificing foreign key integrity for schema flexibility.
Definition
A polymorphic association stores a parent type discriminator and the parent identifier in the child table, instead of a single foreign key to one parent. No real FK constraint can validate the pair, because the id column points at different tables depending on the row. Rails popularized it as commentable or attachable patterns.
Context and scope
Encountered in comment, attachment, activity, and tag systems that must attach to many entity types. The modeling alternative is exclusive arcs, join tables, or class table inheritance.
Common misconceptions
- Polymorphic means flexible and safe — it deletes the database-level referential guarantee; nothing stops dangling ids
- You cannot index it — you can index the pair; you just cannot enforce it, and per-type queries need composite handling
- It is the only way to share a child table — exclusive arcs, supertables, or per-parent join tables model the same need with integrity
Anti-patterns
- Hard-deleting parents and leaving invisible orphaned comments and attachments
- Storing ids that are valid for the wrong type and querying cross-type collisions without noticing
- Querying attachments by joining on id alone, silently matching rows from unrelated parent types
Related concepts
Related examples
Authoritative source
What it is
A foreign key points at exactly one table. When one child table — comments, files, events — must serve many parents, a discriminator plus id column emulates the reference. The price is that the database cannot verify the pair means anything.
How it works
The child row stores parent_type and parent_id; the ORM composes the right table name at query time. Joins become two-step or application-side. Deleting a parent must sweep children explicitly or via ORM callbacks, since no ON DELETE CASCADE exists.
When to accept it
Cross-cutting concerns like activity feeds and tags where integrity loss is tolerable and the alternative multiplies tables. When integrity matters, prefer a common supertable that all parents reference, or exclusive arcs with per-parent nullable foreign keys and CHECK constraints.
Pitfalls
Id collisions across types silently corrupt queries that join on id alone. Per-parent statistics need type filters everywhere. Migration to a cleaner model gets harder as more rows depend on the discriminator values — freeze the enum early and treat it as a contract.