User Guide Help Center Documentation User Forums Training
New LookML
Old LookML
New LookML
Looker
Reusing Code with Extends

This is an advanced topic that assumes a good, pre-existing knowledge of LookML.

As your LookML model expands in size and complexity it becomes increasingly useful to re-use various bits of LookML in multiple places. Looker has the option to “extend” explores, views, and dashboards so that you can do just that.

In essence, when you extend something, you combine its contents with the contents of another LookML object. Then, if there are any conflicts, one of the objects “wins” based on how you’ve written your LookML. Finally, the new, combined LookML is interpreted as normal.

There are lots of reasons you might want to work with extends. Here are just a few:

Example

To help make this more clear, let’s look at a simple example.

explore: transaction { extends: [customer] persist_for: "5 minutes" }   explore: customer { persist_for: "12 hours" }

In this situation you’re creating an explore called transaction, which is based on the explore called customer. You can see that the code kind of reads this way, like “transaction extends what’s already in customer”. Everything that is in customer - like its joins - will be included in transaction. And of course, anything that is in transaction will remain in transaction.

However, you’ll notice there is a conflict. The customer explore says the persist_for setting should be 12 hours, but the transaction explore says it should be 5 minutes. In this situation the transaction explore “wins”, because it is the view that is doing the extending.

Basic Usage

The extends parameter only works with LookML dashboards, views, and explores. Although you can’t extend a model file with the extends parameter, there is a workaround using include described in this User Forum topic.

Extending a LookML Dashboard

Extending a dashboard is the most straightforward extension, and works like you’d expect:

The new, extended dashboard looks like:

- dashboard: name_of_the_new_dashboard extends: name_of_the_dashboard_being_extended # The additional things you want to add or change # on the new dashboard are added as normal

The existing dashboard that is being extended looks like:

- dashboard: name_of_the_dashboard_being_extended # The normal contents of the dashboard follow

Extending a View

When you extend a view it’s a good idea to use the sql_table_name parameter on the view that is going to be extended, if you haven’t done so already. For example:

The new, extended view looks like:

include: "view_file_name.view.lkml" view: name_of_the_new_view { extends: [name_of_the_view_being_extended] # The additional things you want to add or change # on the new view are added as normal }

The existing view that is being extended looks like:

view: name_of_the_view_being_extended { sql_table_name: appropriate_setting_as_explained_next ;; # The normal contents of the view follow }

Remember that sql_table_name defines the table in your database that will be queried by a view, and that its default value is the name of the view. So, if you aren’t already using sql_table_name, simply give it the same value as your view name. The reason that adding sql_table_name is a good idea is explained in more detail below.

You will also notice the use of the include parameter. This may initially seem unecessary, especially if the file has already been included in your model file. However, explicitly declaring the files on which the extended view depends allows Looker to complete more robust error checking. It also prevents future problems if someone were to change the include settings in your model file.

Extending an Explore

When you extend an explore it’s a good idea to use both the view and from parameters on the explore that is going to be extended, if you haven’t done so already. For example:

The new, extended explore looks like:

explore: name_of_the_new_explore { extends: [name_of_the_explore_being_extended] # The additional things you want to add or change # on the new view are added as normal   # IMPORTANT: Relies on the from and view_name parameters # in the explore that is being extended }

The existing view that is being extended looks like:

explore: name_of_the_explore_being_extended { view_name: appropriate_setting_as_explained_next from: appropriate_setting_as_explained_next # The normal contents of the explore follow }

Outside of extends, it would be pretty unusual to use view_name and from at the same time. However, in this context, this is how you can think about their purposes:

The reason that adding these parameters is a good idea is explained in more detail below.

Requiring Extension

For explores, views, and LookML dashboards, there is also an option to use the extension: required parameter if you want to create a starting point that must always be extended by something else in order to be visible to users. For example, maybe you have an explore called Basic Order Fields that should be hidden to users, except when it is extended by other useful information:

# This will make sure that basic_order_fields # doesn’t show up to users all by itself explore: basic_order_fields { extension: required }   # This will appear to users because # basic_order_fields is being extended explore: marketing_order_fields { extends: [basic_order_fields] }

It’s tempting to turn on hidden to hide Basic Order Fields, but remember that such a setting is going to be inherited by any explore that is based on Basic Order Fields. In this particular example, Marketing Order Fields would get hidden as well if you turned on hidden.

Details of Extend Functionality

If you’d like to understand better why we suggest the extra parameters in the Basic Usage of Extends, or start to use extends in more complex or unusual ways, it’s important to know exactly how extends are executed. The four steps are:

  1. A copy is made of the explicitly defined LookML objects inside the dashboard/view/explore that is being extended
  2. The copies are merged with the explicitly defined LookML objects inside the dashboard/view/explore that is doing the extending
  3. If a LookML object is explicitly defined in both places, the object from the extending dashboard/view/explore gets used
  4. The resulting LookML is interpreted as normal

Let’s look at an example, step-by-step, to see the implications of this approach:

Starting LookML

This is the LookML that we’re going to use in our example:

User With Age Extensions View File

view: user_with_age_extensions { extends: [user] suggestions: no dimension: age { type: number sql: ${TABLE}.age ;; } }

User View File

view: user { suggestions: yes dimension: name { sql: ${TABLE}.name ;; } }

Step 1: Copy

In this case the User view is being extended into the User With Age Extensions view. Since User is the view that is being extended, a copy of it is made before merging. Understanding that a copy is made is not particularly important, other than to realize that the original User view is left unchanged, and usable as normal.

Step 2: Merge

The next step is for all of LookML from the extended view (User) to be merged into the extending view (User With Age Extensions). It’s important to understand the nature of this merge, which is simply a merging of YAML hashes (YAML is the language on which LookML is based). In practical terms this means that any explicitly written LookML gets merged, but the default LookML values that you haven’t written down don’t get merged. In a sense, it’s really just the text of the LookML that is getting put together, and none of the meaning of that text.

Step 3: Resolve Conflicts

The third step that occurs is a resolution of any conflicts between the merged views. The extending view is the view that will “win”, which in this case is User With Age Extensions. The fact that default LookML values aren’t being considered yet is important, because you don’t want to make the mistake of thinking that conflicts between default values are getting resolved. In actuality, they’re just being ignored at this step. This is why we suggest in the Basic Usage of Extends that you explicitly add some additional parameters, so that the conflict resolution will occur. In this particular example we have not done that (i.e. we never added sql_table_name to the User view), which is going to cause some problems in the next step.

Step 4: Interpret the LookML

In the final step, the resulting LookML is interpreted as normal, including all the default values. In this particular example we’ve ended up with LookML that includes view: user_with_age_extensions, but no sql_table_name parameter. As a result, Looker is going to assume that the value of sql_table_name is equal to the view name:

The problem is that there is probably no table in our database called user_with_age_extensions. This is why we suggest that you add a sql_table_name parameter to any view that is going to be extended. Adding view and from to explores that will be extended avoids similar problems.

Advanced Usage

Extending More Than One Object At the Same Time

It is possible to extend more than one dashboard, view, or explore at the same time. For example:

explore: orders { extends: [user_info, marketing_info] } # Also works for dashboards and views

The extension process works exactly as described above, but you’ll need to keep one extra rule in mind about how conflicts are resolved. If there are any conflicts between the multiple items listed in the extends parameter, priority is given to the items listed last. So in the example above, if there were conflicts between User Info and Marketing Info, the Marketing Info explore would win.

Chaining Together Multiple Extends

You can also chain together as many extends as you’d like. For example:

explore: orders { extends: [user_info] … } explore: user_info { extends: [marketing_info] … }

Again, the extension process works exactly as described above, with one extra rule about conflict resolution. If there are any conflicts, priority is given to the last item in the chain of extends. In this example:

Additional Options with Lists

When working with lists you may choose to combine them, instead of having the extending object’s list be the winner. Consider this simple extension with a conflicting list called animals:

view: pets { extends: fish set: animals { fields: [dog, cat] } } view: fish { set: animals { fields: [goldfish, guppy] } }

In this case the Pets view is doing the extending, and will therefore win, making animals contain [dog, cat]. However, by making use of the special EXTENDED* set, you can combine the lists instead:

view: pets { extends: fish set: animals { fields: [dog, cat, EXTENDED*] } } view: fish { set: animals { fields: [goldfish, guppy] } }

Now the animals list will contain [dog, cat, goldfish, guppy].

Combining Instead of Replacing During Conflict Resolution

In the basic behavior described above we mentioned that, if there are any conflicts during extension, the extending object wins. For example, take this simple extension:

view: product_short_description { extends: product dimension: description { sql: ${TABLE}.short_description ;; } } view: product { dimension: description { sql: ${TABLE}.full_description ;; } }

You can see there is a conflict of the sql parameter within the Description dimension. Typically, the defintion from Product Short Description will simply overwrite the definition from Product because it is doing the extending.

However, you can also choose to combine the definitions if you like. To do so, you’ll use the ${EXTENDED} keyword like this:

view: product_short_description { extends: product dimension: description { sql: LEFT(${EXTENDED}, 50) ;; } } view: product { dimension: description { sql: ${TABLE}.full_description ;; } }

Now the conflict of the sql parameter will be addressed differently. Instead of the Product Short Description definition winning, it will take the definition from Product and insert it where ${EXTENDED} is used. The resulting definition for Description in this case will be: LEFT(${TABLE}.full_description, 50).

Top