Skip to content
Accessibility palettes

User guide

This guide walks you through the main features of the ddi-l CRUD API. Each section is self-contained, so you can jump to the workflow you need.

Prerequisites

  • Python 3.11 or newer with ddi-l installed (pip install .).
  • No XML knowledge is required for the simple API.

Create a study

Use ddi.new_study() to create a new DDI document with a study unit:

import ddi_l as ddi

doc = ddi.new_study(title="Household Survey", agency="example.org")
print(doc.agency)  # -> example.org

The function returns a Document object. It automatically creates the DDI instance, study unit, and all required identification metadata.

Add questions

Use doc.add_question() to add questions to the study:

q1 = doc.add_question(text="What is your gender?")
q2 = doc.add_question(text="How old are you?")
q3 = doc.add_question(text="What is your household income?")

print(f"Questions: {len(doc.questions)}")  # -> 3

Each question is a QuestionItem in a QuestionScheme inside the DataCollection module.

Add variables

Use doc.add_variable() to add variables, optionally linking them to questions or concepts:

age_concept = doc.add_concept(name="Age")

doc.add_variable(name="Gender", question=q1)
doc.add_variable(name="Age", question=q2, concept=age_concept)
doc.add_variable(name="Household Income", question=q3)

print(f"Variables: {len(doc.variables)}")  # -> 3

When you pass a question= or concept= argument, ddi-l automatically creates the DDI reference linking the variable to that item.

Add concepts and universes

demo_concept = doc.add_concept(name="Demographics")
doc.add_universe(name="Canadian adults aged 18+")

print(f"Concepts: {len(doc.concepts)}")  # -> 2
print(f"Universes: {len(doc.universes)}")  # -> 1

Add code lists

cl = doc.add_code_list(name="Gender Codes")
print(f"Code lists: {len(doc.code_lists)}")  # -> 1

Work with any item type

The add_item() method supports all 30 DDI item types registered in the type registry. Use it for types that do not have a dedicated convenience method:

from ddi_l.models.logicalproduct import Category, RepresentedVariable
from ddi_l.models.datacollection import Instrument

doc.add_item(Category, name="Male")
doc.add_item(Category, name="Female")
doc.add_item(RepresentedVariable, name="Gender Representation")
doc.add_item(Instrument, name="CAWI Questionnaire")

# Query items by type
print(f"Categories: {len(doc.items(Category))}")  # -> 2

Find and remove items

Every item gets a unique identifier when created. Use find() to look up an item by its identifier, and remove() to delete it:

cat = doc.add_item(Category, name="Not specified")
print(cat.identifier)

found = doc.find(cat.identifier)
print(found)  # Prints the Category object

doc.remove(cat.identifier)
print(doc.find(cat.identifier))  # -> None

Save to XML

doc.save("household-survey.xml")

The output uses proper DDI namespace prefixes (r:, s:, d:, l:, c:, a:, p:), not generic ns0/ns1.

Open an existing file

doc = ddi.open_ddi("household-survey.xml")

print(f"Questions: {len(doc.questions)}")
print(f"Variables: {len(doc.variables)}")

for v in doc.variables:
    print(f"  {v.identifier}")

Pass validate=True to run schema validation on load:

doc = ddi.open_ddi("household-survey.xml", validate=True)

Validate

Call doc.validate() to check the document against the DDI schema:

issues = doc.validate()
if issues:
    for issue in issues:
        print(f"[{issue.severity}] {issue.message}")
else:
    print("Document is valid!")

For more validation options, see the validation guide.

Advanced: working with the model layer

The generated model classes under ddi_l.models are the advanced layer. You can import and use them directly for fine-grained control:

from ddi_l.models.base import InternationalString, Reference
from ddi_l.models.logicalproduct import Variable

var = Variable(
    agency="example.org",
    identifier="var-age",
    version="1.0",
    names=[InternationalString(text="Age", lang="en")],
)

See the models reference for a complete list of available types.