Updates using field masks

  • Google Ads API uses field masks for updates, listing fields to be changed.

  • The field_mask helper function in google.api_core is the recommended way to generate field masks.

  • The field_mask helper can compare two protobuf objects or compare a protobuf object to None to generate the mask.

  • The generated field mask object must be copied onto the operation object before sending to the server.

In the Google Ads API, updates are done using a field mask. The field mask lists all the fields you intend to change with the update, and any specified fields that are not in the field mask are ignored, even if sent to the server.

Field mask helper

The recommended way to generate field masks is using the field_mask helper function included in the google.api_core package. It accepts two protobuf objects and returns a field mask object with a paths list that contains all of the fields that are different between the two objects.

If None is passed as the first parameter, then the field mask list contains all of the fields on the second protobuf object that are not set to their default value.

Once constructed, the field mask object should be copied onto the operation object that will be sent to the server.

Update from a new local object

In the following example, you create an empty CampaignOperation object and retrieve an empty Campaign object from its update field. You then modify that campaign object and create a new field mask by comparing it to None, which generates a field mask containing the modified network_settings.target_search_network field:

from google.ads.googleads.client import GoogleAdsClient
from google.api_core import protobuf_helpers

# Retrieve a GoogleAdsClient instance.
client = GoogleAdsClient.load_from_storage()

# Create a new campaign operation.
campaign_operation = client.get_type("CampaignOperation")

# Retrieve a new campaign object from its update field and set its resource
# name.
campaign = campaign_operation.update
campaign.resource_name = client.get_service("CampaignService").campaign_path(
    customer_id, campaign_id
)

# Mutate the campaign (use direct attribute assignment in proto-plus).
campaign.network_settings.target_search_network = False

# Create a field mask using the updated campaign.
# The field_mask helper is compatible with raw protobuf message instances,
# which you can access using the ._pb attribute.
field_mask = protobuf_helpers.field_mask(None, campaign._pb)

# Copy the field_mask onto the operation's update_mask field.
client.copy_from(campaign_operation.update_mask, field_mask)

Update an existing resource

The following example updates an existing campaign retrieved from the API, assuming a valid resource_name and customer_id. With this strategy, updated_campaign shares all the fields retrieved on initial_campaign (including its resource_name), and the generated field mask tells the API that only the network_settings.target_search_network field changed:

from google.ads.googleads.client import GoogleAdsClient
from google.api_core import protobuf_helpers

# Retrieve a GoogleAdsClient instance.
client = GoogleAdsClient.load_from_storage()

# Retrieve an instance of the GoogleAdsService.
googleads_service = client.get_service("GoogleAdsService")

# Search query to retrieve the campaign. Quote string literals in GAQL.
query = f"""
    SELECT
      campaign.network_settings.target_search_network,
      campaign.resource_name
    FROM campaign
    WHERE campaign.resource_name = '{resource_name}'"""

# Submit a query to retrieve a campaign instance.
response = googleads_service.search_stream(
    customer_id=customer_id, query=query
)

# Iterate over results to retrieve the campaign.
initial_campaign = None
for batch in response:
    for row in batch.results:
        initial_campaign = row.campaign
        break

if not initial_campaign:
    raise ValueError(f"Campaign '{resource_name}' not found.")

# Create a new campaign operation.
campaign_operation = client.get_type("CampaignOperation")

# Set the copied campaign object to a variable for easy reference.
updated_campaign = campaign_operation.update

# Copy the retrieved campaign into the new campaign.
# client.copy_from works with both native protobuf messages and messages
# wrapped by the proto-plus library.
client.copy_from(updated_campaign, initial_campaign)

# Mutate the new campaign.
updated_campaign.network_settings.target_search_network = False

# Create a field mask by comparing initial and updated protobuf objects.
field_mask = protobuf_helpers.field_mask(
    initial_campaign._pb, updated_campaign._pb
)

# Copy the field mask onto the operation's update_mask field.
# Note that the client's copy_from method works with both native messages
# and messages wrapped by proto-plus, including google.protobuf.field_mask_pb2.
client.copy_from(campaign_operation.update_mask, field_mask)

Clear fields or set empty messages

When comparing against None, the field_mask helper ignores fields set to empty or default values. To explicitly clear a field or set an empty message field, append the field path directly to campaign_operation.update_mask.paths. For more details, see Set empty message objects as fields.