Using Looker API to copy looks

(Just spreading some awareness, to help save others time in the future. Many thanks to my wonderful Looker Support representative, Annie.)

Copying a look was confusing at first. I had assumed I could just take the `look()` endpoint to grab the body of the existing look, and then use the `create_look()` endpoint to create a copy (and adjust certain inputs if needed, like filters).

Unfortunately, `look()` doesn’t actually return a JSON response. It returns something called a “LookWithQuery” object; this object is JSON-like, but it also has other non-JSON-y objects – like “Query”. On the other hand `create_look()` requires a JSON-dict input. 

So, you can’t just put the response from `look()` into the body of `create_look()`.

What you need to do is extract the “query_id” and “space_id” from from the LookWithQuery object.

  • query_id: a reference to the actual underlying query. This is different from the Look, which is just sort of like some pretty packaging for the query. The Look itself has its own “id”.
  • space_id: it’s essentially a destination folder.

Then, create a new JSON-dictionary with these fields, add a “title”, and add whatever other fields you want to adjust (like “filters”).

Pass this to `create_look()` instead.

import json
import looker_sdk
from looker_sdk import models

sdk = looker_sdk.init31("looker.ini")

look_object = sdk.look(look_id = 1) # or whatever look_id you'll copy
#
# or, you might need to use: sdk.look(look_id = 11250).__dict__
#
# the hidden attribute __dict__ (double underscore) produces the JSON version of that object: https://community.looker.com/looker-api-77/update-look-with-filters-in-python-12589

query_id = look_object["query_id"]
space_id = look_object["space_id"]

json_query = {
"title": "SDK Copy of Look",
"query_id": query_id,
"space_id": space_id
}
y = json.dumps(json_query)

sdk.create_look(y)

Miscellaneous Notes:

  • I think `create_look()` can take both a JSON-dict and JSON-string, so `*.dumps()` might not be necessary.
  • “title” must be different from any other existing look in the destination folder.
  • Yes,  "title", "query_id", and "space_id" are all that is necessary for `create_look()`. Currently, this isn’t clarified in the Looker doc, which indicates multiple other non-read-only fields.
  • I think “space_id” and “folder_id” are exactly the same. "Space" is the old name for "Folder". Annie says: “We used to refer to folders in the Looker UI as spaces. So my guess is that they added the folder_id when we changed the language, but didn't want to deprecate space_id for those who were already using the call with space_id.”
  • You must use a “space_id” / “folder_id” that already currently exists.
0 1 620
1 REPLY 1

Update: 

The previous message still stands.

However, I’ve also been informed that the `WriteLookWithQuery()` method is probably the more preferred/recommended method, in generating the body for `create_look()`

Compared to creating a dictionary, this method is sort of just a more full-proof method / less error-prone than writing our own dictionary: don't need to worry about any syntax issues other than the method itself. I guess it also looks a bit cleaner too, over the manual option.

Also demonstrating with “folder_id” instead of “space_id”.

look_id = 1
destination_folder_id = 17

look = sdk.look(look_id)

new_look = sdk.create_look(looker_sdk.models.WriteLookWithQuery(
title=look.title,
query_id=look.query_id,
folder_id=destination_folder_id))

print(new_look)