diff --git a/src/reference/definition-syntax.md b/src/reference/definition-syntax.md index f4b67a24..1a89faa1 100644 --- a/src/reference/definition-syntax.md +++ b/src/reference/definition-syntax.md @@ -21,7 +21,7 @@ class TableName(dj.Manual): ``` definition = [comment] pk_section "---" secondary_section -pk_section = attribute_line+ +pk_section = attribute_line* secondary_section = attribute_line* attribute_line = [foreign_key | attribute] @@ -36,6 +36,35 @@ core_type = int32 | float64 | varchar(n) | ... codec_type = "<" name ["@" [store]] ">" ``` +## Singleton Tables + +!!! version-added "New in 2.1" + + Singleton tables were introduced in DataJoint 2.1. + +The primary key section may be empty. A table declared that way is a **singleton**: it holds at +most one row, which is what you want for global configuration, pipeline-wide parameters, or a +single summary. Start the definition with the `---` separator and declare only secondary +attributes. + +```python +@schema +class Config(dj.Lookup): + definition = """ + # Global configuration + --- + setting1 : varchar(100) + setting2 : int32 + """ +``` + +`insert1` takes no key, a second insert raises `DuplicateError`, `fetch1()` returns the row, and +`heading.primary_key` is `[]`. Internally the table carries a hidden `_singleton` attribute as +its key, which is excluded from the heading, from `fetch()` results, and from join matching. + +See [Table Declaration](specs/table-declaration.md#25-singleton-tables-empty-primary-keys) for +the full behavior. + ## Foreign Keys ```python @@ -149,7 +178,6 @@ class Session(dj.Manual): DataJoint validates definitions at declaration time: -- Primary key must have at least one attribute - Attribute names must be valid identifiers - Types must be recognized - Foreign key references must exist diff --git a/src/reference/specs/fetch-api.md b/src/reference/specs/fetch-api.md index 9b91d2cc..6ee38ea1 100644 --- a/src/reference/specs/fetch-api.md +++ b/src/reference/specs/fetch-api.md @@ -66,7 +66,7 @@ for row in table: ### Basic Fetch Operations -| Old Pattern (1.x) | New Pattern (2.0) | +| Old Pattern (pre-2.0) | New Pattern (2.0) | |-------------------|-------------------| | `table.fetch()` | `table.to_arrays()` or `table.to_dicts()` | | `table.fetch(format="array")` | `table.to_arrays()` | @@ -75,7 +75,7 @@ for row in table: ### Attribute Fetching -| Old Pattern (1.x) | New Pattern (2.0) | +| Old Pattern (pre-2.0) | New Pattern (2.0) | |-------------------|-------------------| | `table.fetch('a')` | `table.to_arrays('a')` | | `a, b = table.fetch('a', 'b')` | `a, b = table.to_arrays('a', 'b')` | @@ -83,7 +83,7 @@ for row in table: ### Primary Key Fetching -| Old Pattern (1.x) | New Pattern (2.0) | +| Old Pattern (pre-2.0) | New Pattern (2.0) | |-------------------|-------------------| | `table.fetch('KEY')` | `table.keys()` | | `table.fetch(dj.key)` | `table.keys()` | @@ -100,7 +100,7 @@ a = table.to_arrays('a') ### Ordering, Limiting, Offset -| Old Pattern (1.x) | New Pattern (2.0) | +| Old Pattern (pre-2.0) | New Pattern (2.0) | |-------------------|-------------------| | `table.fetch(order_by='name')` | `table.to_arrays(order_by='name')` | | `table.fetch(limit=10)` | `table.to_arrays(limit=10)` | @@ -108,7 +108,7 @@ a = table.to_arrays('a') ### Single Row Fetch (fetch1) -| Old Pattern (1.x) | New Pattern (2.0) | +| Old Pattern (pre-2.0) | New Pattern (2.0) | |-------------------|-------------------| | `table.fetch1()` | `table.fetch1()` (unchanged) | | `a, b = table.fetch1('a', 'b')` | `a, b = table.fetch1('a', 'b')` (unchanged) | @@ -116,14 +116,14 @@ a = table.to_arrays('a') ### Configuration -| Old Pattern (1.x) | New Pattern (2.0) | +| Old Pattern (pre-2.0) | New Pattern (2.0) | |-------------------|-------------------| | `dj.config['fetch_format'] = 'frame'` | Use `.to_pandas()` explicitly | | `with dj.config.override(fetch_format='frame'):` | Use `.to_pandas()` in the block | ### Iteration -| Old Pattern (1.x) | New Pattern (2.0) | +| Old Pattern (pre-2.0) | New Pattern (2.0) | |-------------------|-------------------| | `for row in table:` | `for row in table:` (same syntax, now lazy!) | | `list(table)` | `table.to_dicts()` | @@ -254,7 +254,7 @@ table = Experiment().to_arrow() The new iteration is significantly more efficient: ```python -# Old (1.x): N+1 queries +# Old (pre-2.0): N+1 queries # 1. fetch("KEY") gets ALL keys # 2. fetch1() for EACH key