Note: we'll be using our home-built API connector Create an API connector. We could simplify this by using the 'official' Unicat client lib, but that wouldn't give the API insights because it is more high-level.
Note: at the end of the page, we show the same example while using the Unicat client lib.
We'll start by fetching the root record, and then walk through the entire records tree depth-first. First though, we establish the connection.
from ccapi import UnicatApi
from .onfig import PROJECT_GID, SECRET_API_KEY
ccapi = UnicatApi("https://unicat.app", PROJECT_GID)
success, result = ccapi.connect(SECRET_API_KEY)
if not success:
print(result)
import sys
sys.exit(1)
Then, retrieve the root. To keep thing simple, we assume every call is successful. In reality, life's not always simple though.
succes, result = ccapi.call("/records/root", { "language": "en" })
root_record = ccapi.data["records"][result["root"]]
print(root_record["title"])
We can use the root to fetch its children.
succes, result = ccapi.call("/records/children", { "language": "en", "record": root_record["gid"] })
for child_record_gid in result["children"]:
child_record = ccapi.data["records"][child_record_gid]
print(child_record["title"])
If we can call that recursively, we can walk the whole tree, so let's put this in a function.
def walk_tree_depth_first(ccapi, parent_gid):
succes, result = ccapi.call("/records/children", { "language": "en", "record": parent_gid })
if not result["children"]:
return
for child_record_gid in result["children"]:
child_record = ccapi.data["records"][child_record_gid]
print(" " * child_record["treelevel"], "-", child_record["title"]) # add indent
walk_tree_depth_first(ccapi, child_record_gid)
You can specify the channel for some API calls, but let's do it manually here. I'm using channel key "650ebd0f-eab5-41d7-87c1-1dc5334af872" here.
channel_key = "650ebd0f-eab5-41d7-87c1-1dc5334af872"
def walk_tree_depth_first(ccapi, parent_gid)
succes, result = ccapi.call("/records/children", { "language": "en", "record": parent_gid })
if not result["children"]:
return
for child_record_gid in result["children"]:
child_record = ccapi.data["records"][child_record_gid]
if channel_key not in child_record["channels"]: # manually skip disabled record
continue
print(" " * child_record["treelevel"], "-", child_record["title"])
walk_tree_depth_first(ccapi, child_record_gid)
Children can be ordered according to user-defined criteria. We're using ordering key "87b53a4e-359d-45c7-81cb-5e1c3ef444ec" in the following example - you can find these keys in the project options..
ordering_key = "87b53a4e-359d-45c7-81cb-5e1c3ef444ec"
def walk_tree_depth_first(ccapi, parent_gid)
data = { "language": "en",
"record": parent_gid,
"ordering": ordering_key, # added an ordering here
}
succes, result = ccapi.call("/records/children", data)
if not result["children"]:
return
for child_record_gid in result["children"]:
child_record = ccapi.data["records"][child_record_gid]
if channel_key not in child_record["channels"]:
continue
print(" " * child_record["treelevel"], "-", child_record["title"])
walk_tree_depth_first(ccapi, child_record_gid)
You know a record doesn't have any children if the call returns an empty list and you didn't specify a channel filter. This costs an API call though, and you already have the parent record, which knows how many children it has (unfiltered only) in its childcount
property. Let's use that.
def walk_tree_depth_first(ccapi, parent_gid)
data = { "language": "en",
"record": parent_gid,
"ordering": ordering_key,
}
succes, result = ccapi.call("/records/children", data)
if not result["children"]:
return
for child_record_gid in result["children"]:
child_record = ccapi.data["records"][child_record_gid]
if channel_key not in child_record["channels"]:
continue
print(" " * child_record["treelevel"], "-", child_record["title"])
if child_record["childcount"] > 0: # added a childcount check
walk_tree_depth_first(ccapi, child_record_gid)
The children
call can potentially return a very large number of records. The API allows you to paginate through them, using page.top
and page.size
parameters. The thing to be aware of, is that the default for page.size
is 100, so if you have records that have more than a hundred children, you should either specify a higher page.size
value, or perform multiple calls with updated page.top
values.
Note: the default value for
page.size
is 100, so beware if you have records with more children than that.
How do you know that there are more than 100 children? The result for the /records/children
call includes a children.size
value.
Note: pagination requires ordering
def walk_tree_depth_first(ccapi, parent_gid)
top = 0
size = 100
while True:
data = { "language": "en",
"record": parent_gid,
"ordering": ordering_key,
"page.top": top, # include explicit pagination
"page.size": size,
}
succes, result = ccapi.call("/records/children", data)
if not result["children"]:
return
for child_record_gid in result["children"]:
child_record = ccapi.data["records"][child_record_gid]
if channel_key not in child_record["channels"]:
continue
print(" " * child_record["treelevel"], "-", child_record["title"])
if child_record["childcount"] > 0:
walk_tree_depth_first(ccapi, child_record_gid)
top+= size
if top >= result["children.size"]: # know when to stop
break
pip install unicat
Ok, here it goes:
from unicat import Unicat
from .config import PROJECT_GID, SECRET_API_KEY, LOCAL_ASSET_FOLDER
unicat = Unicat("https://unicat.app", PROJECT_GID, SECRET_API_KEY, LOCAL_ASSET_FOLDER)
if not unicat.connect():
raise Exception("Invalid connection settings")
channel = unicat.project.channels("Main channel")
ordering = unicat.project.orderings("Main ordering")
for record in unicat.walk_record_tree(channel=channel, ordering=ordering):
print(record.title)
All the complexity for channels, orderings, and result data structures are hidden behind a fluent interface.
Documentation for the Unicat client lib is available here.