Question

Update_look with filters in Python

  • 5 June 2019
  • 8 replies
  • 573 views

I am trying to use the update_look command in order to update the filter of a saved look with a new list of IDs. Is there an example of what the correct way to enter my filter name and content into the update_look request?

I have been referring to this page but I could use a little help on what to put in the “filter” spot in the JSON in order to make the filter update how I want.


8 replies

Userlevel 7
Badge +1

Replied to you over here!


Hi @izzy, I was trying to follow your recommendations and I cant drop the id from the Query element.

Im using the official Looker Python API and the return is not an string neither a dictionary, so how can I modify that Query body for the create_query function?


best.

Userlevel 1

I am so glad to see I’m not the only one running into this problem! It sounds like you’re using the official looker_sdk package. And you’re not getting the expected (JSON) object type in the response when you get the look. I am trying to do exactly the same thing you are: change the filters in a look via the API. I’ve raised this issue in several places since last week, and have not received a good answer yet. It sounds to me like a bug in the SDK. In the meantime, I am going to sidestep the SDK and use the information I found here that shows some examples of something similar without the SDK. Their code base is available on GitHub here.


I hope that helps until they fix the SDK

Hi @nanotellez I (think) was able to modify the response object by simply assign, like look.query = new_query, but Im still having problems with the update_look function. I tried in different ways but still raising this error: SDKError: {"message":"Not found","documentation_url":"http://docs.looker.com/"} even that the function exist on SDK.

Userlevel 1

I think I’ve figured this out (with the help of James from Looker Customer support). Yes, you are correct that you can access the fields of the LookWithQuery class like that, e.g. look.query to get the query. But that query is also in the wrong format. For some reason the SDK is returning these classes, but still requires JSON objects to process changes. So here’s the trick: the classes have a hidden attribute __dict__ (double underscore) that produces the JSON version of that object. Here’s the function I built to process the query to modify its filters


def update_query_filters(api_query, changes):
# Get the JSON version of the query
query = api_query.__dict__

# In order to create a new query, read_only fields need to be removed
# Filter config also needs to be removed otherwise it will override the filter options in the ui
read_only_fields = [
"id",
"client_id",
"slug",
"share_url",
"url",
"expanded_share_url",
"has_table_calculations",
"can",
"filter_config"
]

for field in read_only_fields:
if field in query:
query.pop(field)

#Extract the filters from the query
filters = query['filters']
# the parameter 'changes' is a dictionary of keys: new_values so we update the filters dictionary
filters.update(changes)
# Update the query dictionary with the new filters
query.update({"filters": filters})

# Returns a JSON object ready to create a new query
return query

With this, I can update a look like this:


# get old query from the look

old_look = sdk.look(your_look_id)

old_query = old_look.query

# change region filter, get a JSON new_query in the process

new_query = update_query_filters(old_query, {key1: new_value1, key2: new_value2})

# create a new query in looker with the new filter

new_API_query = sdk.create_query(new_query)

# To modify the look's filters, we just need to pass the new query id

new_query_id = new_API_query.id

# update the look

updated_look = sdk.update_look(your_look_id, {'query_id':new_query_id})

look_results = sdk.run_look(your_look_id, 'json') # These do come back in proper JSON format

I just tested it and it works! Why there aren’t readily available examples of this kind of thing already? Beats me. Seems to me like one of the most basic things one would want to do to create a website powered by Looker… I spent days banging my head on a wall to get to this point.

I didnt noticed that you replied me, but I was able to make this creating some functions after reading Fivetran methods, but at the end is the same. I didnt get any help or answers from Looker support but your first reply with Fivetran helped me. Maybe in the future I will use the SDK but now that I already made some progress I will continue with the custom methods and functions that I have.


Thanks!

Thanks for the solution!


I was able to make it work without accessing the class dict attribute as follows, but it just follows the same pattern you laid out.



from looker_sdk import client, models, error, methods

# Create looker client
looker_client = client.setup("looker.ini")

#Get Look
look = looker_client.look(2935)

#Modify Query Filter
look.query.filters['filter_name'] = 'new_filter_value'

#Write New Query
write_query = models.WriteQuery(model=look.query.model,
view=look.query.view,
fields=look.query.fields,
filters=look.query.filters,
vis_config=look.query.vis_config,
filter_config=look.query.filter_config,
query_timezone=look.query.query_timezone)

#Create New Query
new_query = looker_client.create_query(write_query)

#Write Updated Look
write_look = models.WriteLookWithQuery(title=look.title,
deleted=look.deleted,
query_id=new_query.id,
space=look.space,
folder=look.folder,
space_id=look.space_id,
folder_id=look.folder_id,
user_id=look.user_id,
query=new_query)

#Update Look
updated_look = looker_client.update_look(2935, write_look)

I think I’ve figured this out (with the help of James from Looker Customer support). Yes, you are correct that you can access the fields of the LookWithQuery class like that, e.g. look.query to get the query. But that query is also in the wrong format. For some reason the SDK is returning these classes, but still requires JSON objects to process changes. So here’s the trick: the classes have a hidden attribute __dict__ (double underscore) that produces the JSON version of that object. Here’s the function I built to process the query to modify its filters

 

def update_query_filters(api_query, changes):

# Get the JSON version of the query

query = api_query.__dict__



# In order to create a new query, read_only fields need to be removed

# Filter config also needs to be removed otherwise it will override the filter options in the ui

read_only_fields = [

"id",

"client_id",

"slug",

"share_url",

"url",

"expanded_share_url",

"has_table_calculations",

"can",

"filter_config"

]



for field in read_only_fields:

if field in query:

query.pop(field)



#Extract the filters from the query

filters = query['filters']

# the parameter 'changes' is a dictionary of keys: new_values so we update the filters dictionary

filters.update(changes)

# Update the query dictionary with the new filters

query.update({"filters": filters})



# Returns a JSON object ready to create a new query

return query

 

With this, I can update a look like this:

 

 

# get old query from the look



old_look = sdk.look(your_look_id)



old_query = old_look.query



# change region filter, get a JSON new_query in the process



new_query = update_query_filters(old_query, {key1: new_value1, key2: new_value2})



# create a new query in looker with the new filter



new_API_query = sdk.create_query(new_query)



# To modify the look's filters, we just need to pass the new query id



new_query_id = new_API_query.id



# update the look



updated_look = sdk.update_look(your_look_id, {'query_id':new_query_id})



look_results = sdk.run_look(your_look_id, 'json') # These do come back in proper JSON format

 

I just tested it and it works! Why there aren’t readily available examples of this kind of thing already? Beats me. Seems to me like one of the most basic things one would want to do to create a website powered by Looker… I spent days banging my head on a wall to get to this point.

 

Thank you for sending this, really helpful! But the column name is not following the label I put in the viz. Anyone can help?

Reply