Setting Empty Message Objects as Fields

In the Google Ads API some message fields are defined as empty message objects, such as campaign.manual_cpm, or they may only have optional fields that don't need to be set, for example campaign.manual_cpc. Setting these fields is important to tell the API which bidding strategy to use for the given Campaign, but it's not intuitive when the messages are empty.

When updating the campaign.name field, which is a string, we set the field by updating it directly as if it were a normal Python object attribute:

campaign.name = "Test campaign value"

campaign.manual_cpc is a nested field, meaning it contains another protobuf message and not a primitive type, like a string. You can update its fields directly as well:

campaign.manual_cpc.enhanced_cpc_enabled = True

This will tell the API that this Campaign has a bidding strategy of manual_cpc with enhanced CPC enabled.

But what if you want to use manual_cpm, which is empty? Or manual_cpc without enabling enhanced cpc? To do this you will need to copy a separate empty instance of the class onto the campaign, for example:

client = GoogleAdsClient.load_from_storage()

empty_cpm = client.get_type('ManualCpm')
client.copy_from(campaign.manual_cpm, empty_cpm)

Note how manual_cpm is specified for the campaign object:

name {
  value: "Test campaign value"
}
manual_cpm {
}

The manual_cpm field is set, but none of its fields have values. When sending request to the API that use this pattern, you can verify that you're setting the empty message object correctly by enabling logging and inspecting the request payload.

Lastly, you'll need to manually add this field to the request object's update_mask. The field mask helper has no mechanism to determine the difference between a field that's been explicitly set to an empty object, and a field that hasn't been set.

from google.api_core.protobuf_helpers import field_mask

campaign_operation.create = campaign
campaign_operation.update_mask = field_mask(None, campaign)
# Here we manually add the "manual_cpm" field
campaign_operation.update_mask.append("manual_cpm")