pythondataframesalesforcesimple-salesforce

Python Simple Salesforce : Auto populate AccountID in Contact for imported Contacts


I am importing 10 Contacts from an external data source into Salesforce using Simple Salesforce Python library.

Background : One Account can have multiple records in Contact. I have created an ExternalAccountID fields in both Account and Contact. The accounts related to the 10 Contacts being imported has been already loaded into the Account object. The 'ExternalAccountID' in the Account object contains the 'legacy account id'(e.g 1234_LegacyAccountId).

The 'ExternalAccountID' field in Account is 'External' and Unique. The 'ExternalAccountID' in Contact is not-unique, as there can be multiple contacts per account.

The 'ExternalAccountContactID' in Contact is 'External' and 'Unique'. This field consists of a concat of legacy Account ID + Legacy Contact ID. This field is used to Upsert the Contact data.

Problem: The 'Id' from Account object is not being populated automatically in the AccountId field of the 'Contact' object, the accounts for the 10 contacts is already available in the Account object.

The current solution uses Pandas Dataframe to upsert the 10 contacts. Here is the code snippet to query the source data and upsert the data in Salesforce target server.

    information = sf.query_all(query= sql_code)
    table = pandas.DataFrame(information['records']).drop(columns='attributes')
    table['ExternalAccountID'] = table.Id 
    table['ExternalAccountContactID']=(table['AccountId'].astype(str)) +"_"+ 
    (table['Id'].astype(str))
    new_df = table[['Name','Email', 'ExternalAccountID', 'Department']]
    new_df = new_df.rename(columns= 
    {"ExternalAccountID":"ExternalAccountID__c","Name":"Name","Email":"Email", 
    "Department":"Department"})  
    results_json = new_df.to_json(orient='records')
    records_upsert = json.loads(results_json)
    print ("Records to be upserted")
    sft.bulk.Contact.upsert(records_upsert,'ExternalAccountContactID__c'
    ,batch_size=10000,use_serial=True)

Where should I specify in the script that the related Account object needs to be referred so that the 'Id' from Account can be retrieved ? In Data Loader I am able to upsert the data and the 'AccountId' in Contact is being auto populated populated, how do I achieve the same result using Python ? Any hint


Solution

  • Assuming the ext id on Account is 1234_LegacyAccountId and on Contact is 1234_LegacyAccountId_1_Contact.

    The raw REST API request you need will be something like

    https://yourInstance.salesforce.com/services/data/v52.0/sobjects/Contact/ExternalAccountContactID__c/1234_LegacyAccountId_1_Contact

    {
       "FirstName" : "Joe",
       "LastName" : "Bloggs",
       "Email" : "example@example.com",
       "Account" :
       {
           "ExternalAccountID__c" : "1234_LegacyAccountId" 
       }
    }
    

    It's written up a bit here: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_upsert.htm

    Now you need to figure out how to represent it in simple/pandas. Maybe start by naming the column Account:ExternalAccountID__c (or with dot). Maybe pandas allows objects as column payload?

    Worst case you could make the REST call yourself, just stealing from simple's the login method to get server and session ID and craft the REST call manually. See https://github.com/simple-salesforce/simple-salesforce#additional-features

    This style of upsert works 1 record at a time (because you pass contact's ext id in the url, you can't send multiple contacts as payload). Once you master the single call you could batch them for up to 25 in single request (from what I remember, might have been increased), bit like my answer https://salesforce.stackexchange.com/a/274696/799

    And there's also https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_graph_introduction.htm but I don't think simple supports that, looks bit black magic.