Containers — CRUD Operations
Flywheel organizes data in a strict hierarchy: groups contain projects, projects contain subjects, subjects contain sessions, and sessions contain acquisitions. Each level is a container — an object that holds data and child containers. The Python SDK (software development kit) represents every container as a typed Python object with a consistent set of methods for creating, reading, updating, and deleting data.
This page explains how to perform CRUD (Create, Read, Update, Delete) operations on containers using the SDK. For a full description of the hierarchy itself and what each container type represents, see Core Concepts: Hierarchy.
Container models in the SDK
When you fetch a container from Flywheel, the SDK returns a typed object — for example, a Project, Subject, Session, or Acquisition. These objects expose both the container's data as attributes (subject.label, session.age) and a set of methods for working with the server (subject.update(), session.reload()).
All container objects share a common base that provides update(), reload(), and ref(). Type-specific methods such as add_session() or add_subject() are defined on each container class.
Reading containers
Fetch by ID
The most direct way to retrieve a container is by its unique identifier.
project = fw.get_project("000123456789abcdefABCDEF")
subject = fw.get_subject("000123456789abcdefABCDEF")
session = fw.get_session("000123456789abcdefABCDEF")
acquisition = fw.get_acquisition("000123456789abcdefABCDEF")
Use fw.get(id) when you have an ID but do not know the container type — the SDK resolves the type automatically.
Fetch by path
Use fw.lookup() to resolve a container by its full path in the hierarchy.
project = fw.lookup("my-group/my-project")
session = fw.lookup("my-group/my-project/sub-01/ses-01")
The path follows the pattern group/project/subject/session/acquisition. See Getting Started for more on authentication and client initialization.
Reloading a stale object
When you call update() or after a gear runs, the local Python object is out of date — it reflects the state at the time of the original fetch. Call reload() to get a fresh copy from the server.
subject.update(label="sub-001")
subject = subject.reload() # local object is now current
print(subject.label) # "sub-001"
Always reload an acquisition before accessing its .files list if you need the latest file metadata. The files list on a cached acquisition object may be incomplete.
acq = session.acquisitions.find_first()
acq = acq.reload()
file = acq.files[0]
Listing children
Every container exposes its direct children through Finder attributes: project.subjects, subject.sessions, session.acquisitions. Calling these attributes returns a Finder object — it does not immediately fetch data. Data is fetched when you iterate, call find(), or call find_first().
# Fetch all subjects in a project
subjects = list(project.subjects())
# Filter by label
matching = project.subjects.find("label=sub-001")
# Find the first match
first = project.subjects.find_first()
# Lazy iteration — fetches pages as needed
for subject in project.subjects.iter():
print(subject.label)
Finders support filter expressions, pagination, and lazy iteration. For the full interface, see Core Concepts: Finders.
Creating containers
Each container type has an add_* method on its parent. These methods accept either keyword arguments or a dictionary. They create the child on the server and return the fully populated child object.
Projects
Projects are children of groups. Use fw.add_project() to create one.
project_id = fw.add_project({"label": "My Project", "group": "my-group"})
project = fw.get_project(project_id)
Subjects
# Keyword arguments
subject = project.add_subject(label="sub-01")
# Dictionary
subject_02 = project.add_subject({"label": "sub-02", "type": "human", "sex": "male"})
Subject fields include label, firstname, lastname, sex, type, species, strain, race, ethnicity, date_of_birth, code, and info.
Sessions
Session fields include label, age, weight, operator, timestamp, and info.
Acquisitions
Acquisition fields include label, timestamp, timezone, uid, and info.
Updating containers
Call update() on any container object to write changes to the server. You can pass keyword arguments or a dictionary.
# Rename a project with kwargs
project.update(label="Renamed Project")
# Update a subject with a dictionary
subject.update({"type": "human", "sex": "female"})
# Update a subject with kwargs
subject.update(label="sub-001", firstname="Jane")
update() sends only the fields you provide — it does not overwrite fields you omit. After calling update(), reload the object if you need the current server state.
Mutable fields by container type
| Container | Mutable fields |
|---|---|
| Project | label, description, info |
| Subject | label, firstname, lastname, sex, type, species, strain, race, ethnicity, date_of_birth, code, info |
| Session | label, age, weight, operator, timestamp, timezone, uid, info |
| Acquisition | label, timestamp, timezone, uid, info |
For structured metadata stored in the info field, use update_info() instead of update(). See Core Concepts: Metadata for details on info, tags, and notes.
Moving containers
To move a container to a different parent, call update() with the parent container type as the key and the new parent's ID as the value.
session.update({"subject": subject_02.id})
session = session.reload() # Reload after moving for updated parent references
print(session.subject) # View updated parent subject
new_project_id = fw.add_project({"label": "Another Project", "group": "my-group"})
subject_02.update({"project": new_project_id})
Deleting containers
Use the top-level client methods to delete a container by ID.
fw.delete_acquisition(acquisition.id)
fw.delete_session(session.id)
fw.delete_subject(subject.id)
fw.delete_project(project.id)
Danger
Deletion is permanent and cascades. Deleting a container removes all of its children recursively — every subject, session, acquisition, file, and analysis it contains. This cannot be undone by users in any way, through the SDK or the UI. For emergencies, deleted data can generally be recovered by Flywheel support for a limited time after deletion. Verify the target container before deleting.
# Confirm before deleting
print(f"Deleting session: {session.label} ({session.id})")
fw.delete_session(session.id)
The get-or-create pattern
When writing scripts that run repeatedly or process incoming data, you often need to ensure a container exists before writing to it — without creating duplicates. The get-or-create pattern finds the container if it exists, or creates it if it does not.
def get_or_create_project(fw, group, label):
existing = fw.projects.find_first(f"label={label},group={group}")
if existing:
return existing
new_id = fw.add_project({"label": label, "group": group})
return fw.get_project(new_id)
def get_or_create_subject(project, label):
existing = project.subjects.find_first(f"label={label}")
if existing:
return existing
return project.add_subject({"label": label})
Note that fw.add_project() returns an ID string, not a full project object. The call to fw.get_project(new_project) fetches the complete object. project.add_subject() returns the full subject object directly, so no second fetch is needed.
This pattern is safe to run concurrently only if you tolerate a small race window. For strict idempotency at scale, use server-side upsert endpoints.
Quick reference
| Operation | Method |
|---|---|
| Get container by ID | fw.get_project(id), fw.get_subject(id), fw.get_session(id), fw.get_acquisition(id) |
| Get by path | fw.lookup("group/project/subject/session") |
| Reload stale object | container = container.reload() |
| List children | list(project.subjects()) |
| Filter children | project.subjects.find("label=sub-01") |
| Create project | fw.add_project({"label": "...", "group": "..."}) |
| Create subject | project.add_subject(label="sub-01") |
| Create session | subject.add_session(label="ses-01") |
| Create acquisition | session.add_acquisition(label="T1w") |
| Update container | container.update(label="new-label") |
| Move container | container.update({"<parent_type>": new_parent_id}) |
| Delete container | fw.delete_session(session.id) |