Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions src/how-to/define-tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,22 +253,29 @@ class TaskType(dj.Lookup):

### Manual or Lookup?

Both tiers hold rows that are *entered* rather than computed, so the question is
**where a row comes from**:

- Use **`dj.Lookup`** when the rows are part of the schema's design and belong in
the code — parameter sets, method definitions, controlled vocabularies,
enumerations. They are declared in `contents`, versioned with the table, and
identical in every deployment until the code changes. Updating a Lookup means
editing `contents` and redeploying — the change flows through your normal CI/CD
Both tiers hold rows that are *entered* rather than computed. The deciding
question is **which process is accountable for the rows' quality: code review, or
data management?**

- Use **`dj.Lookup`** when the rows are **part of the schema definition** and their
quality is guaranteed by **code review before deployment**. They live in
`contents`, are versioned with the table, and are identical in every deployment
until the code changes — parameter sets, method definitions, enumerations, and
controlled vocabularies *when code-seeded*. Updating a Lookup means editing
`contents` and redeploying, so the change flows through your normal CI/CD
process, not a runtime insert.
- Use **`dj.Manual`** when the rows are entered at runtime and are specific to a
project or experiment — subjects, sessions, samples, or anything typed into a
form, ingested from a file, or read from an instrument.

If you find yourself populating a "Lookup" table at runtime (for example, from a
dashboard form) rather than from its committed `contents`, make it `dj.Manual`
instead — its rows are runtime data, not part of the schema definition.
- Use **`dj.Manual`** when the rows are **populated at runtime** and their quality
is guaranteed by the **data-management process** — validation, curation, and
access control at ingest, not code review. Subjects, sessions, samples, or
anything typed into a form, ingested from a file, or read from an instrument.

This resolves the case that "where a row comes from" leaves ambiguous: a controlled
vocabulary that is **populated at runtime** — gene symbols loaded from an external
reference, ontology terms discovered during processing. It reads like a Lookup, but
its rows are runtime data governed by the data-management process, so it is
`dj.Manual`. It still gets domain integrity from a `unique index` on its term; it
simply is not code-reviewed schema content. Make `gene_symbol` a `dj.Lookup` only
when you seed a fixed, reviewed list in `contents`.

## Part Tables

Expand Down
4 changes: 2 additions & 2 deletions src/reference/specs/table-declaration.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class TableName(dj.Manual):

| Tier | Base Class | Table Prefix | Purpose |
|------|------------|--------------|---------|
| Manual | `dj.Manual` | (none) | Data inserted directly from outside the pipeline (users, instruments, ingestion scripts) |
| Lookup | `dj.Lookup` | `#` | Reference data defined in the schema via `contents` |
| Manual | `dj.Manual` | (none) | Data inserted at runtime from outside the pipeline (users, instruments, ingestion scripts); quality assured by the data-management process |
| Lookup | `dj.Lookup` | `#` | Reference data defined in the schema via `contents`; quality assured by code review |
| Imported | `dj.Imported` | `_` | Populated by `make()` from an external source |
| Computed | `dj.Computed` | `__` | Derived from other tables |
| Part | `dj.Part` | `master__` | Detail records of master table |
Expand Down
Loading