UIDs¶
Client-side 11-char DHIS2 UID generator + validator. Same algorithm as org.hisp.dhis.common.CodeGenerator.java (leading letter + 10 alphanumeric chars, secrets-driven entropy), so values minted locally are accepted by every DHIS2 write endpoint without a server-side rename. Avoids a /api/system/id round-trip.
When to reach for it¶
- Pre-generating UIDs for parent + child objects before the parent is created (the
programTrackedEntityAttributesjoin table is one example — DHIS2 imports it as part of the parent's PUT body, so the child UID has to exist client-side first). - Building a bulk metadata bundle where every object's
idis known up front (save_bulk/import_bundleflows). - Snapshotting a future-state UID into a fixture file for round-trip tests.
The two read-side helpers (generate_uids(count) for bulk, is_valid_uid(s) for guarding wire data) complete the surface.
Worked example¶
from dhis2w_client import generate_uid, generate_uids, is_valid_uid
# Single fresh UID, perfect for one-off metadata creates.
new_id = generate_uid()
assert is_valid_uid(new_id)
print(new_id) # e.g. 'aB3xY7zK1pq' — 11 chars, starts with a letter
# 100 distinct UIDs for a bulk import.
ids = generate_uids(100)
assert len(set(ids)) == 100
# Guard wire data before treating a string as a UID.
candidate = "user-friendly-name"
if not is_valid_uid(candidate):
print(f"{candidate!r} is not a valid DHIS2 UID — needs the 11-char shape")
Pre-generating linked UIDs¶
The typical use is "I want to PUT a parent that references children that don't exist yet":
from dhis2w_client import DataElement, generate_uid
from dhis2w_core.client_context import open_client
from dhis2w_core.profile import profile_from_env
new_de_id = generate_uid()
new_dataset_id = generate_uid()
async with open_client(profile_from_env()) as client:
# Create the DE with the pre-generated ID...
await client.data_elements.create(
uid=new_de_id, name="My DE", short_name="MyDE", value_type="INTEGER",
)
# ...then create the DataSet that references it.
await client.data_sets.create(
uid=new_dataset_id, name="My DS", short_name="MyDS",
period_type="Monthly", data_elements=[new_de_id],
)
Property-based round-trip tests of generate_uid / generate_uids / is_valid_uid live in packages/dhis2w-client/tests/test_parser_properties.py.
uids
¶
Client-side DHIS2 UID generator — mirrors dhis2w-core/CodeGenerator.java.
DHIS2 UIDs are 11-character strings over [0-9A-Za-z]. The first character
is always a letter ([A-Za-z]), the remaining ten are any alphanumeric.
The regex ^[A-Za-z][A-Za-z0-9]{10}$ is the canonical validation form,
used across the DHIS2 codebase.
Upstream reference
dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/CodeGenerator.java
Generating client-side avoids a round-trip to /api/system/id for every
new UID — useful when minting bulk payloads for /api/metadata imports.
Uses secrets.choice (CSPRNG) so UIDs are unguessable — which matters for
share-with-user workflows where the UID acts as a capability.
Attributes¶
UID_LENGTH = 11
module-attribute
¶
Fixed DHIS2 UID length (11 characters).
UID_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
module-attribute
¶
First-character alphabet — 52 letters, upper + lower.
UID_ALPHABET = '0123456789' + UID_LETTERS
module-attribute
¶
Full 62-character alphanumeric alphabet — digits + upper + lower.
UID_RE = re.compile('^[A-Za-z][A-Za-z0-9]{10}$')
module-attribute
¶
Canonical DHIS2 UID regex — matches what dhis2w-core validates on write.
Functions¶
generate_uid()
¶
Return one fresh DHIS2 UID.
The first character is drawn from the 52-letter alphabet; the remaining
ten from the 62-character alphanumeric set. Uses secrets.choice for
cryptographic randomness (matches dhis2w-core/CodeGenerator.java's
security-sensitive path).
Source code in packages/dhis2w-client/src/dhis2w_client/v42/uids.py
generate_uids(count)
¶
Return count fresh DHIS2 UIDs. Collisions are statistically negligible.