Skip to content

Manuals — Models

Overview

The manuals app defines a structured content model for building rich, ordered manuals.

The model layer is responsible for:

  • content structure
  • relationships
  • ordering rules
  • validation constraints
  • data integrity

It does not handle:

  • workflow logic
  • request handling
  • serialization
  • side effects

High-level structure

Manual content is hierarchical, but also partially stream-based.

Hierarchy:

  • Organization
  • Manual
    • Chapter
    • Sections / Notes / StepGroups / Media / Callouts

Display:

  • ChapterItem defines the ordered stream inside a chapter

Model relationships (high-level)

flowchart TD

Org --> Manual
Manual --> Chapter

Chapter --> Section
Chapter --> SectionNote
Chapter --> StepGroup

StepGroup --> ManualStep

Manual --> Callout
Chapter --> Callout
Section --> Callout
StepGroup --> Callout
ManualStep --> Callout

Manual --> ManualMedia
Chapter --> ManualMedia
Section --> ManualMedia
StepGroup --> ManualMedia
ManualStep --> ManualMedia

Chapter --> ChapterItem
ChapterItem --> Section
ChapterItem --> SectionNote
ChapterItem --> Callout
ChapterItem --> StepGroup

Taxonomy models

Category

Represents a high-level grouping for manuals.

Fields:

  • org
  • name
  • slug

Constraints:

  • unique (org, slug)

Notes:

  • org-scoped
  • used for filtering and organization

Topic

Secondary taxonomy layer.

Fields:

  • org
  • name
  • slug
  • color

Constraints:

  • unique (org, slug)

Notes:

  • adds visual metadata (color)
  • more flexible than category

Core content models

Manual

Top-level content container.

Fields:

  • org
  • title
  • description
  • cover_image
  • category
  • topic
  • is_public_external
  • published_at
  • order

Indexes:

  • (org, order, id)
  • (org, published_at, id)

Notes:

  • belongs to exactly one org
  • contains ordered chapters
  • can be externally published

Chapter

Second-level container inside a manual.

Fields:

  • manual
  • order
  • title
  • description
  • cover_image

Constraints:

  • unique (manual, order)

Indexes:

  • (manual, order)

Notes:

  • defines logical grouping of content
  • owns the display stream via ChapterItem

Content block models

Section

Rich content block.

Fields:

  • chapter
  • order
  • title
  • description
  • description_json

Indexes:

  • (chapter, order)

Notes:

  • description_json is the primary rich content source
  • supports inline media references

SectionNote

Lightweight note block.

Fields:

  • chapter
  • order
  • title
  • body

Indexes:

  • (chapter, order)

Notes:

  • simpler than section
  • useful for quick annotations

StepGroup

Container for ordered steps.

Fields:

  • chapter
  • order
  • title

Indexes:

  • (chapter, order)

ManualStep

Individual step inside a step group.

Fields:

  • step_group
  • order
  • title
  • body_text

Constraints:

  • unique (step_group, order)

Indexes:

  • (step_group, order)

Notes:

  • represents procedural instructions

Callout models

CalloutPreset

Reusable callout style.

Fields:

  • org
  • name
  • slug
  • icon
  • color
  • is_active
  • order

Constraints:

  • unique (org, slug)

Notes:

  • centralizes styling
  • enables consistent UI

Callout

Contextual highlight block.

Fields:

  • manual (optional)
  • chapter (optional)
  • section (optional)
  • step_group (optional)
  • step (optional)
  • order
  • name
  • description
  • preset
  • icon
  • color

Indexes:

  • per-parent (parent, order)

Callout validation

A callout must be attached to exactly one parent.

flowchart TD

Start --> CountParents
CountParents -->|0| ErrorNone
CountParents -->|>1| ErrorMultiple
CountParents -->|1| Valid

Callout parent rules

Rules:

  • zero parents → invalid
  • multiple parents → invalid
  • exactly one → valid

Resolved fields

Callout resolves display properties:

  • icon_resolved
  • color_resolved

Resolution order:

  1. explicit value on callout
  2. value from preset
  3. empty string

Media model

ManualMedia

Represents media attached to content.

Fields:

  • manual (optional)
  • chapter (optional)
  • section (optional)
  • step_group (optional)
  • step (optional)
  • order
  • file
  • caption
  • type (image, video, file)

Indexes:

  • per-parent (parent, order)

Media attachment rule

Media must belong to exactly one parent.

This is enforced at serializer/service level.


ChapterItem (display stream)

Purpose

Defines the ordered rendering stream of a chapter.

Separates:

  • storage models
  • display order

Fields

  • manual
  • chapter
  • order
  • kind

Targets:

  • section
  • note
  • callout
  • step_group

Supported kinds

  • section
  • note
  • callout
  • step_group
  • media_grid

Validation rules

flowchart TD

Start --> CheckKind

CheckKind --> SectionCheck
CheckKind --> NoteCheck
CheckKind --> CalloutCheck
CheckKind --> StepGroupCheck
CheckKind --> MediaGridCheck

SectionCheck --> RequireSection
NoteCheck --> RequireNote
CalloutCheck --> RequireCallout
StepGroupCheck --> RequireStepGroup
MediaGridCheck --> NoTarget

RequireSection --> Valid
RequireNote --> Valid
RequireCallout --> Valid
RequireStepGroup --> Valid
NoTarget --> Valid

Rules

  • each kind requires exactly one matching target
  • media_grid requires no target
  • mismatches raise validation errors

Additional constraints

  • callout must belong to same chapter
  • step_group must belong to same chapter

Ordering strategy

Ordering is enforced via:

  • order fields on all content models
  • unique constraints where necessary
  • explicit ordering in Meta

Important:

  • ChapterItem.order defines actual display order
  • other order fields define internal ordering

Data integrity principles

Single parent rule

Applies to:

  • Callout
  • ManualMedia

Ensures:

  • no ambiguous ownership
  • predictable queries

Chapter-local consistency

Some objects must belong to the same chapter:

  • callout in ChapterItem
  • step_group in ChapterItem

Org scoping

Manual is org-scoped.

All related data inherits org through:

  • manual → chapter → content

Design decisions

Split storage vs display

Content models store data.

ChapterItem controls display order.

This allows:

  • reordering without rewriting content
  • flexible layouts

Rich content via JSON

Sections use structured JSON instead of HTML.

Benefits:

  • safer editing
  • easier transformation
  • better frontend integration

Flexible attachment model

Callouts and media attach to multiple levels.

This enables:

  • global manual highlights
  • chapter-level context
  • step-level detail

Summary

The models layer defines:

  • hierarchical structure (manual → chapter → content)
  • flexible attachment system (media, callouts)
  • ordered rendering (ChapterItem)
  • org-scoped ownership
  • strong validation rules

It is the foundation for:

  • serializers (data shape)
  • services (workflow)
  • views (API exposure)

Key idea

store content once, control presentation separately, enforce strict structure.