Giter VIP home page Giter VIP logo

simple-salesforce's Introduction

Simple Salesforce

image

Documentation Status

Simple Salesforce is a basic Salesforce.com REST API client built for Python 3.8, 3.9, 3.10, 3.11, and 3.12. The goal is to provide a very low-level interface to the REST Resource and APEX API, returning a dictionary of the API JSON response. You can find out more regarding the format of the results in the Official Salesforce.com REST API Documentation


Documentation

Official Simple Salesforce documentation

Examples

There are two ways to gain access to Salesforce

The first is to simply pass the domain of your Salesforce instance and an access token straight to Salesforce()

For example:

from simple_salesforce import Salesforce
sf = Salesforce(instance='na1.salesforce.com', session_id='')

If you have the full URL of your instance (perhaps including the schema, as is included in the OAuth2 request process), you can pass that in instead using instance_url:

from simple_salesforce import Salesforce
sf = Salesforce(instance_url='https://na1.salesforce.com', session_id='')

There are also four means of authentication, one that uses username, password and security token; one that uses IP filtering, username, password and organizationId, one that uses a private key to sign a JWT, and one for connected apps that uses username, password, consumer key, and consumer secret;

To login using the security token method, simply include the Salesforce method and pass in your Salesforce username, password and token (this is usually provided when you change your password):

from simple_salesforce import Salesforce
sf = Salesforce(username='[email protected]', password='password', security_token='token')

To login using IP-whitelist Organization ID method, simply use your Salesforce username, password and organizationId:

from simple_salesforce import Salesforce
sf = Salesforce(password='password', username='[email protected]', organizationId='OrgId')

To login using the JWT method, use your Salesforce username, consumer key from your app, and private key (How To):

from simple_salesforce import Salesforce
sf = Salesforce(username='[email protected]', consumer_key='XYZ', privatekey_file='filename.key')

To login using a connected app, simply include the Salesforce method and pass in your Salesforce username, password, consumer_key and consumer_secret (the consumer key and consumer secret are provided when you setup your connected app):

from simple_salesforce import Salesforce
sf = Salesforce(username='[email protected]', password='password', consumer_key='consumer_key', consumer_secret='consumer_secret')

If you'd like to enter a sandbox, simply add domain='test' to your Salesforce() call.

For example:

from simple_salesforce import Salesforce
sf = Salesforce(username='[email protected]', password='password', security_token='token', domain='test')

Note that specifying if you want to use a domain is only necessary if you are using the built-in username/password/security token authentication and is used exclusively during the authentication step.

If you'd like to keep track where your API calls are coming from, simply add client_id='My App' to your Salesforce() call.

from simple_salesforce import Salesforce
sf = Salesforce(username='[email protected]', password='password', security_token='token', client_id='My App', domain='test')

If you view the API calls in your Salesforce instance by Client Id it will be prefixed with simple-salesforce/, for example simple-salesforce/My App.

When instantiating a Salesforce object, it's also possible to include an instance of requests.Session. This is to allow for specialized session handling not otherwise exposed by simple_salesforce.

For example:

from simple_salesforce import Salesforce
import requests

session = requests.Session()
# manipulate the session instance (optional)
sf = Salesforce(
   username='[email protected]', password='password', organizationId='OrgId',
   session=session)

Record Management

To create a new 'Contact' in Salesforce:

sf.Contact.create({'LastName':'Smith','Email':'[email protected]'})

This will return a dictionary such as {u'errors': [], u'id': u'003e0000003GuNXAA0', u'success': True}

To get a dictionary with all the information regarding that record, use:

contact = sf.Contact.get('003e0000003GuNXAA0')

To get a dictionary with all the information regarding that record, using a custom field that was defined as External ID:

contact = sf.Contact.get_by_custom_id('My_Custom_ID__c', '22')

To change that contact's last name from 'Smith' to 'Jones' and add a first name of 'John' use:

sf.Contact.update('003e0000003GuNXAA0',{'LastName': 'Jones', 'FirstName': 'John'})

To delete the contact:

sf.Contact.delete('003e0000003GuNXAA0')

To retrieve a list of Contact records deleted over the past 10 days (datetimes are required to be in UTC):

import pytz
import datetime
end = datetime.datetime.now(pytz.UTC)  # we need to use UTC as salesforce API requires this!
sf.Contact.deleted(end - datetime.timedelta(days=10), end)

To retrieve a list of Contact records updated over the past 10 days (datetimes are required to be in UTC):

import pytz
import datetime
end = datetime.datetime.now(pytz.UTC) # we need to use UTC as salesforce API requires this
sf.Contact.updated(end - datetime.timedelta(days=10), end)

Note that Update, Delete and Upsert actions return the associated Salesforce HTTP Status Code

Use the same format to create any record, including 'Account', 'Opportunity', and 'Lead'. Make sure to have all the required fields for any entry. The Salesforce API has all objects found under 'Reference -> Standard Objects' and the required fields can be found there.

Queries

It's also possible to write select queries in Salesforce Object Query Language (SOQL) and search queries in Salesforce Object Search Language (SOSL).

All SOQL queries are supported and parent/child relationships can be queried using the standard format (Parent__r.FieldName). SOQL queries are done via:

sf.query("SELECT Id, Email, ParentAccount.Name FROM Contact WHERE LastName = 'Jones'")

If, due to an especially large result, Salesforce adds a nextRecordsUrl to your query result, such as "nextRecordsUrl" : "/services/data/v26.0/query/01gD0000002HU6KIAW-2000", you can pull the additional results with either the ID or the full URL (if using the full URL, you must pass 'True' as your second argument)

sf.query_more("01gD0000002HU6KIAW-2000")
sf.query_more("/services/data/v26.0/query/01gD0000002HU6KIAW-2000", True)

As a convenience, to retrieve all of the results in a single local method call use

sf.query_all("SELECT Id, Email FROM Contact WHERE LastName = 'Jones'")

While query_all materializes the whole result into a Python list, query_all_iter returns an iterator, which allows you to lazily process each element separately

data = sf.query_all_iter("SELECT Id, Email FROM Contact WHERE LastName = 'Jones'")
for row in data:
  process(row)

Values used in SOQL queries can be quoted and escaped using format_soql:

sf.query(format_soql("SELECT Id, Email FROM Contact WHERE LastName = {}", "Jones"))
sf.query(format_soql("SELECT Id, Email FROM Contact WHERE LastName = {last_name}", last_name="Jones"))
sf.query(format_soql("SELECT Id, Email FROM Contact WHERE LastName IN {names}", names=["Smith", "Jones"]))

To skip quoting and escaping for one value while still using the format string, use :literal:

sf.query(format_soql("SELECT Id, Email FROM Contact WHERE Income > {:literal}", "USD100"))

To escape a substring used in a LIKE expression while being able to use % around it, use :like:

sf.query(format_soql("SELECT Id, Email FROM Contact WHERE Name LIKE '{:like}%'", "Jones"))

SOSL queries are done via:

sf.search("FIND {Jones}")

There is also 'Quick Search', which inserts your query inside the {} in the SOSL syntax. Be careful, there is no escaping!

sf.quick_search("Jones")

Search and Quick Search return None if there are no records, otherwise they return a dictionary of search results.

More details about syntax is available on the Salesforce Query Language Documentation Developer Website

CRUD Metadata API Calls

You can use simple_salesforce to make CRUD (Create, Read, Update and Delete) API calls to the metadata API.

First, get the metadata API object:

mdapi = sf.mdapi

To create a new metadata component in Salesforce, define the metadata component using the metadata types reference given in Salesforce's metadata API documentation

custom_object = mdapi.CustomObject(
    fullName = "CustomObject__c",
    label = "Custom Object",
    pluralLabel = "Custom Objects",
    nameField = mdapi.CustomField(
        label = "Name",
        type = mdapi.FieldType("Text")
    ),
    deploymentStatus = mdapi.DeploymentStatus("Deployed"),
    sharingModel = mdapi.SharingModel("Read")
)

This custom object metadata can then be created in Salesforce using the createMetadata API call:

mdapi.CustomObject.create(custom_object)

Similarly, any metadata type can be created in Salesforce using the syntax mdapi.MetadataType.create(). It is also possible to create more than one metadata component in Salesforce with a single createMetadata API call. This can be done by passing a list of metadata definitions to mdapi.MetadataType.create(). Up to 10 metadata components of the same metadata type can be created in a single API call (This limit is 200 in the case of CustomMetadata and CustomApplication).

readMetadata, updateMetadata, upsertMetadata, deleteMetadata, renameMetadata and describeValueType API calls can be performed with similar syntax to createMetadata:

describe_response = mdapi.CustomObject.describe()
custom_object = mdapi.CustomObject.read("CustomObject__c")
custom_object.sharingModel = mdapi.SharingModel("ReadWrite")
mdapi.CustomObject.update(custom_object)
mdapi.CustomObject.rename("CustomObject__c", "CustomObject2__c")
mdapi.CustomObject.delete("CustomObject2__c")

The describe method returns a DescribeValueTypeResult object.

Just like with the createMetadata API call, multiple metadata components can be dealt with in a single API call for all CRUD operations by passing a list to their respective methods. In the case of readMetadata, if multiple components are read in a single API call, a list will be returned.

simple_salesforce validates the response received from Salesforce. Create, update, upsert, delete and rename methods return None, but raise an Exception with error message (from Salesforce) if Salesforce does not return success. So, error handling can be done by catching the python exception.

simple_salesforce also supports describeMetadata and listMetadata API calls as follows. describeMetadata uses the API version set for the Salesforce object and will return a DescribeMetadataResult object.

mdapi.describe()
query = mdapi.ListMetadataQuery(type='CustomObject')
query_response = mdapi.list_metadata(query)

Up to 3 ListMetadataQuery objects can be submitted in one list_metadata API call by passing a list. The list_metadata method returns a list of FileProperties objects.

File Based Metadata API Calls

You can use simple_salesforce to make file-based calls to the Metadata API, to deploy a zip file to an org.

First, convert and zip the file with:

sfdx force:source:convert -r src/folder_name -d dx

Then navigate into the converted folder and zip it up:

zip -r -X package.zip *

Then you can use this to deploy that zipfile:

result = sf.deploy("path/to/zip", sandbox=False, **kwargs)
asyncId = result.get('asyncId')
state = result.get('state')

Both deploy and checkDeployStatus take keyword arguments. The single package argument is not currently available to be set for deployments. More details on the deploy options can be found at https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_deploy.htm

You can check on the progress of the deploy which returns a dictionary with status, state_detail, deployment_detail, unit_test_detail:

sf.checkDeployStatus(asyncId)

Example of a use-case:

from simple_salesforce import Salesforce

deployment_finished = False
successful = False

sf = Salesforce(session_id="id", instance="instance")
sf.deploy("path/to/zip", sandbox=False ,**kwargs)

while not deployment_finished:
    result = sf.checkDeployStatus(asyncId)
    if result.get('status') in ["Succeeded", "Completed", "Error", "Failed", None]:
        deployment_finished = True
    if result.get('status') in ["Succeeded", "Completed"]:
        successful = True

if successful:
    print("โœ…")
else:
    print("๐Ÿฅ”")

Other Options

To insert or update (upsert) a record using an external ID, use:

sf.Contact.upsert('customExtIdField__c/11999',{'LastName': 'Smith','Email': '[email protected]'})

To format an external ID that could contain non-URL-safe characters, use:

external_id = format_external_id('customExtIdField__c', 'this/that & the other')

To retrieve basic metadata use:

sf.Contact.metadata()

To retrieve a description of the object, use:

sf.Contact.describe()

To retrieve a description of the record layout of an object by its record layout unique id, use:

sf.Contact.describe_layout('39wmxcw9r23r492')

To retrieve a list of top level description of instance metadata, user:

sf.describe()

for x in sf.describe()["sobjects"]:
  print x["label"]

Using Bulk

You can use this library to access Bulk API functions. The data element can be a list of records of any size and by default batch sizes are 10,000 records and run in parallel concurrency mode. To set the batch size for insert, upsert, delete, hard_delete, and update use the batch_size argument. To set the concurrency mode for the salesforce job the use_serial argument can be set to use_serial=True.

Create new records:

data = [
      {'LastName':'Smith','Email':'[email protected]'},
      {'LastName':'Jones','Email':'[email protected]'}
    ]

sf.bulk.Contact.insert(data,batch_size=10000,use_serial=True)

Update existing records:

data = [
      {'Id': '0000000000AAAAA', 'Email': '[email protected]'},
      {'Id': '0000000000BBBBB', 'Email': '[email protected]'}
    ]

sf.bulk.Contact.update(data,batch_size=10000,use_serial=True)

Update existing records and update lookup fields from an external id field:

data = [
      {'Id': '0000000000AAAAA', 'Custom_Object__r': {'Email__c':'[email protected]'}},
      {'Id': '0000000000BBBBB', 'Custom_Object__r': {'Email__c': '[email protected]'}}
    ]

sf.bulk.Contact.update(data,batch_size=10000,use_serial=True)

Upsert records:

data = [
      {'Id': '0000000000AAAAA', 'Email': '[email protected]'},
      {'Email': '[email protected]'}
    ]

sf.bulk.Contact.upsert(data, 'Id', batch_size=10000, use_serial=True)

Query records:

query = 'SELECT Id, Name FROM Account LIMIT 10'

sf.bulk.Account.query(query)

To retrieve large amounts of data, use

query = 'SELECT Id, Name FROM Account'

# generator on the results page
fetch_results = sf.bulk.Account.query(query, lazy_operation=True)

# the generator provides the list of results for every call to next()
all_results = []
for list_results in fetch_results:
  all_results.extend(list_results)

Query all records:

QueryAll will return records that have been deleted because of a merge or delete. QueryAll will also return information about archived Task and Event records.

query = 'SELECT Id, Name FROM Account LIMIT 10'

sf.bulk.Account.query_all(query)

To retrieve large amounts of data, use

query = 'SELECT Id, Name FROM Account'

# generator on the results page
fetch_results = sf.bulk.Account.query_all(query, lazy_operation=True)

# the generator provides the list of results for every call to next()
all_results = []
for list_results in fetch_results:
  all_results.extend(list_results)

Delete records (soft deletion):

data = [{'Id': '0000000000AAAAA'}]

sf.bulk.Contact.delete(data,batch_size=10000,use_serial=True)

Hard deletion:

data = [{'Id': '0000000000BBBBB'}]

sf.bulk.Contact.hard_delete(data,batch_size=10000,use_serial=True)

Using Bulk 2.0

You can use this library to access Bulk 2.0 API functions.

Create new records:

"Custom_Id__c","AccountId","Email","FirstName","LastName"
"CustomID1","ID-13","[email protected]","Bob","x"
"CustomID2","ID-24","[email protected]","Alice","y"
...
sf.bulk2.Contact.insert("./sample.csv", batch_size=10000)

Create new records concurrently:

sf.bulk2.Contact.insert("./sample.csv", batch_size=10000, concurrency=10)

Update existing records:

"Custom_Id__c","AccountId","Email","FirstName","LastName"
"CustomID1","ID-13","[email protected]","Bob","X"
"CustomID2","ID-24","[email protected]","Alice","Y"
...
sf.bulk2.Contact.update("./sample.csv")

Upsert records:

"Custom_Id__c","LastName"
"CustomID1","X"
"CustomID2","Y"
...
sf.bulk2.Contact.upsert("./sample.csv", 'Custom_Id__c')

Query records:

query = 'SELECT Id, Name FROM Account LIMIT 100000'

results = sf.bulk2.Account.query(
    query, max_records=50000, column_delimiter="COMM", line_ending="LF"
)
for i, data in enumerate(results):
    with open(f"results/part-{1}.csv", "w") as bos:
        bos.write(data)

Download records(low memory usage):

query = 'SELECT Id, Name FROM Account'

sf.bulk2.Account.download(
    query, path="results/", max_records=200000
)

Delete records (soft deletion):

"Id"
"0000000000AAAAA"
"0000000000BBBBB"
...
sf.bulk2.Contact.delete("./sample.csv")

Hard deletion:

sf.bulk2.Contact.hard_delete("./sample.csv")

Retrieve failed/successful/unprocessed records for ingest(insert,update...) job:

results = sf.bulk2.Contact.insert("./sample.csv")
# [{"numberRecordsFailed": 123, "numberRecordsProcessed": 2000, "numberRecordsTotal": 2000, "job_id": "Job-1"}, ...]
for result in results:
    job_id = result['job_id']
    # also available: get_unprocessed_records, get_successful_records
    data = sf.bulk2.Contact.get_failed_records(job_id)
    # or save to file
    sf.bulk2.Contact.get_failed_records(job_id, file=f'{job_id}.csv')

Using Apex

You can also use this library to call custom Apex methods:

payload = {
  "activity": [
    {"user": "12345", "action": "update page", "time": "2014-04-21T13:00:15Z"}
  ]
}
result = sf.apexecute('User/Activity', method='POST', data=payload)

This would call the endpoint https://<instance>.salesforce.com/services/apexrest/User/Activity with data= as the body content encoded with json.dumps

You can read more about Apex on the Force.com Apex Code Developer's Guide

Additional Features

There are a few helper classes that are used internally and available to you.

Included in them are SalesforceLogin, which takes in a username, password, security token, optional version and optional domain and returns a tuple of (session_id, sf_instance) where session_id is the session ID to use for authentication to Salesforce and sf_instance is the domain of the instance of Salesforce to use for the session.

For example, to use SalesforceLogin for a sandbox account you'd use:

from simple_salesforce import SalesforceLogin
session_id, instance = SalesforceLogin(
    username='[email protected]',
    password='password',
    security_token='token',
    domain='test')

Simply leave off the final domain if you do not wish to use a sandbox.

Also exposed is the SFType class, which is used internally by the __getattr__() method in the Salesforce() class and represents a specific SObject type. SFType requires object_name (i.e. Contact), session_id (an authentication ID), sf_instance (hostname of your Salesforce instance), and an optional sf_version

To add a Contact using the default version of the API you'd use:

from simple_salesforce import SFType
contact = SFType('Contact','sesssionid','na1.salesforce.com')
contact.create({'LastName':'Smith','Email':'[email protected]'})

To use a proxy server between your client and the SalesForce endpoint, use the proxies argument when creating SalesForce object. The proxy argument is the same as what requests uses, a map of scheme to proxy URL:

proxies = {
  "http": "http://10.10.1.10:3128",
  "https": "http://10.10.1.10:1080",
}
SalesForce(instance='na1.salesforce.com', session_id='', proxies=proxies)

All results are returned as JSON converted OrderedDict to preserve order of keys from REST responses.

Helpful Datetime Resources

A list of helpful resources when working with datetime/dates from Salesforce

Convert SFDC Datetime to Datetime or Date object .. code-block:: python

import datetime # Formatting to SFDC datetime formatted_datetime = datetime.datetime.strptime(x, "%Y-%m-%dT%H:%M:%S.%f%z")

#Formatting to SFDC date formatted_date = datetime.strptime(x, "%Y-%m-%d")

Helpful Pandas Resources

A list of helpful resources when working with Pandas and simple-salesforce

Generate list for SFDC Query "IN" operations from a Pandas Dataframe

import pandas as pd

df = pd.DataFrame([{'Id':1},{'Id':2},{'Id':3}])
   def dataframe_to_sfdc_list(df,column):
     df_list = df[column].unique()
     df_list = [str(x) for x in df_list]
     df_list = ','.join("'"+item+"'" for item in df_list)
     return df_list

  sf.query(format_soql("SELECT Id, Email FROM Contact WHERE Id IN ({})", dataframe_to_sfdc_list(df,column)))

Generate Pandas Dataframe from SFDC API Query (ex.query,query_all)

import pandas as pd

sf.query("SELECT Id, Email FROM Contact")

df = pd.DataFrame(data['records']).drop(['attributes'],axis=1)

Generate Pandas Dataframe from SFDC API Query (ex.query,query_all) and append related fields from query to data frame

import pandas as pd

def sf_api_query(data):
 df = pd.DataFrame(data['records']).drop('attributes', axis=1)
 listColumns = list(df.columns)
 for col in listColumns:
     if any (isinstance (df[col].values[i], dict) for i in range(0, len(df[col].values))):
         df = pd.concat([df.drop(columns=[col]),df[col].apply(pd.Series,dtype=df[col].dtype).drop('attributes',axis=1).add_prefix(col+'.')],axis=1)
         new_columns = np.setdiff1d(df.columns, listColumns)
         for i in new_columns:
             listColumns.append(i)
 return df

df = sf_api_query(sf.query("SELECT Id, Email,ParentAccount.Name FROM Contact"))

Generate Pandas Dataframe from SFDC Bulk API Query (ex.bulk.Account.query)

import pandas as pd

sf.bulk.Account.query("SELECT Id, Email FROM Contact")
df = pd.DataFrame.from_dict(data,orient='columns').drop('attributes',axis=1)

YouTube Tutorial

Here is a helpful YouTube tutorial which shows how you can manage records in bulk using a jupyter notebook, simple-salesforce and pandas.

This can be a effective way to manage records, and perform simple operations like reassigning accounts, deleting test records, inserting new records, etc...

Authors & License

This package is released under an open source Apache 2.0 license. Simple-Salesforce was originally written by Nick Catalano but most newer features and bugfixes come from community contributors. Pull requests submitted to the GitHub Repo are highly encouraged!

Authentication mechanisms were adapted from Dave Wingate's RestForce and licensed under a MIT license

The latest build status can be found at Travis CI

simple-salesforce's People

Contributors

ajoys avatar andscoop avatar bastir85 avatar countergram avatar demianbrecht avatar digitalaun avatar fr-anthony avatar guettli avatar hello-josh avatar jasonschroeder-sfdc avatar jgillmanjr avatar jon-wobken avatar kokes avatar leonardoam avatar llazzaro avatar malsmith avatar maxmorlocke avatar nbessi avatar nickcatal avatar olgakogan avatar petergtam avatar raymondr avatar rmonk-redhat avatar scottpersinger avatar shawalli avatar tayloramurphy avatar tedmiston avatar thommor avatar tsabat avatar zackz-woolloo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

simple-salesforce's Issues

Add version information to library.

Currently, the package version is accessible through pkg_resources. It is relatively common, however, to have the version accessible through the package itself. This should be added to simple_salesforce and used to populate the data used in setuptools.setup.

Documentation, querying sobject field history

Hi all,

This is not an issue with the code but something that may be valuable to add to the documentation as it took me a while to get it sorted; The Salesforce documentation and v29 REST API query structure for getting historical information about an SObject are not accurate. I am not sure if this is a bug or the manifestation of different behaviors on the SF backend.

The Salesforce documentation gives an example where the query should look something like
SELECT OldValue, NewValue, Parent.Id, Parent.name, Parent.customfield__c FROM foo__history to get the history of customfield__c of SObject foo. The APEX query is supposed to look something like SELECT ParentId, OldValue, NewValue, Field, CreatedById, CreatedDate FROM Customer_History where parentId = :CustomerId and Field ='Address__c' according to other documentation I found

However, the only way I was able to get the history was a query like SELECT name, Field__c, New_Value__c, Old_Value__c, Id, Modified_By__c, Modify_Date__c From foo_History__c. The query can be refined to limit the history of a specific filed by appending AND (Field__c = 'foo1' OR Field__c = 'foo2') (this would only return the history of foo1 and foo2 fields). Other conditionals, date comparisons, etc, also seem to work fine.

I thought it may be helpful to document this for other users of the simple-salesforce module.

minor doco error

sf = Salesforce(username='[email protected]', password='password', password_token='token', sandbox=True)

should be security_token instead of password_token?

Add unit tests

Some unit tests would be nice and make me (and probably others) more comfortable with using this in production.

Add Accept-Encoding: gzip as a standard header

Dear developers,

Do you think it would be OK to add Accept-Encoding: gzip a standard HTTP header in every request against Salesforce?

Python's requests library is capable of handling gzipped responses, and Salesforce can easily zip them if we add the above-mentioned header.

https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_rest_compression.htm

I think this would be a great optimization as it would save a lot of bandwidth.

If you agree, I can send you a pull request with that change.

Result Error

Hi i newbie in Python i use this code but have this error:

Internal Server Error

The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.

Use this code and create the contact in SF but dont have the result:

contact = sf.Contact.create({'LastName':'Smith','Email':'[email protected]'})
return contact

Thanks

Request: Id Growth Formula on utils

If you are working in a case-sensitive system, the first 15 characters is enough for it to be a unique Id.
if you have the 15 character Id you can generate the last three characters to compose the 18 character Id.

Let me know if anyone disagress into adding this formula to utils, I can code it by myself.

JSON Error

Sorry, kind of new to Python and not sure how to get past this:

In [4]: contact = sf.Contact.get('003A000000dOaZh')

TypeError Traceback (most recent call last)
in ()
----> 1 contact = sf.Contact.get('003A000000dOaZh')

/home/lhenkel/code/sf/simple-salesforce/simple_salesforce/api.pyc in get(self, record_id)
331 """
332 result = self._call_salesforce('GET', self.base_url + record_id)
--> 333 return result.json(object_pairs_hook=OrderedDict)
334
335 def create(self, data):

TypeError: json() got an unexpected keyword argument 'object_pairs_hook'

I'm using Python 2.7.4. I've got simplejson installed and upgraded (according to pip).

I installed from git clone https://github.com/neworganizing/simple-salesforce.git and ran setup.py

Pass error fields to exception

Currently, API errors are handed off to Exceptions, but only after being formatted into the message field as a string, which forces folks to use regex to get at them.

Attach these values to the error, rather than locking them away in message.

Eror with LIKE Query

Hey Guys,
I am using LIKE Operator for my query, but getting error, Also didn't found any example of usage of LIKE query -

responses = sf.query_all("SELECT Name, Access_Key__c, Image_Vendor__c FROM Opportunity WHERE Image_Vendor__c LIKE 'Getty%'")

Error

Malformed request https://na9.salesforce.com/services/data/v29.0/query/?q=SELECT+Name%2C+Access_Key__c%2C+Image_Vendor__c+FROM+Opportunity+WHERE+Image_Vendor__c+LIKE+%27Getty%25%27. Response content: [{u'errorCode': u'INVALID_QUERY_FILTER_OPERATOR', u'message': u"\nImage_Vendor__c FROM Opportunity WHERE Image_Vendor__c LIKE 'Getty%'\n                                       ^\nERROR at Row:1:Column:68\ninvalid operator on multipicklist field"}]

Can anyone help ?

No support for proxies

I need to go through a proxy server to access salesforce. Could we pass in proxy object to all requests?

query_all sums totalSize of partial results. doesn't need to.

Code
qry = "SELECT name, email from lead"
rs = sf.query(qry)

print 'partials.......'
running = 0
while True:
    running += len(rs['records'])
    print rs['done'], rs['totalSize'], len(rs['records']), running
    if rs['done']: break
    rs = sf.query_more(rs['nextRecordsUrl'], True)

print 'query_all......'
rs = sf.query_all(qry)
print rs['done'], rs['totalSize'], len(rs['records'])
Example
$ py sfdc.py
partials.......
False 15007 2000 2000
False 15007 2000 4000
False 15007 2000 6000
False 15007 2000 8000
False 15007 2000 10000
False 15007 2000 12000
False 15007 2000 14000
True 15007 1007 15007
query_all......
True 120056 15007

type error in query

Hi, just trying this for the first time on my company sandbox. I am able to connect, however running a simple query results in the following error.

I am using Anaconda, have tried on both windows 7 and OSX mountain lion and called from the ipython notebook.


TypeError Traceback (most recent call last)
in ()
----> 1 sf.query('SELECT Id, Email FROM Contact')

/Users/Chris/anaconda/lib/python2.7/site-packages/simple_salesforce-0.51-py2.7.egg/simple_salesforce/api.pyc in query(self, query)
156 _exception_handler(result)
157
--> 158 return result.json()
159
160 def query_more(self, next_records_identifier, identifier_is_url=False):

TypeError: 'dict' object is not callable

Use DEFAULT_API_VERSION in SFType

class SFType(object):
    """An interface to a specific type of SObject"""
    def __init__(self, object_name, session_id, sf_instance, sf_version='27.0', proxies=None):

needs to be

class SFType(object):
    """An interface to a specific type of SObject"""
    def __init__(self, object_name, session_id, sf_instance, sf_version=DEFAULT_API_VERSION, proxies=None):

INVALID_QUERY_LOCATOR

I'm getting an error that occurs only occasionally when I run query_all for some SOQL queries. It looks like we are exceeding the 10 allowable query cursors as specified by https://help.salesforce.com/apex/HTViewSolution?id=000004410&language=en_US
I saw in the code for query_all that there isn't any handling for keeping the cursors alive or anything, was wondering if this error had popped up for anyone previously. Right now it happens about 5-6% of the time (on an admittedly small sample), holding the development environment and SOQL query constant.
Trace:
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 359, in query_all
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 353, in get_all_results
return get_all_results(result, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 347, in get_all_results
identifier_is_url=True, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 314, in query_more
_exception_handler(result)
File "/usr/local/lib/python2.7/dist-packages/simple_salesforce/api.py", line 642, in _exception_handler
raise exc_cls(result.url, result.status_code, name, response_content)
SalesforceMalformedRequest: Malformed request https://na14.salesforce.com/services/data/v29.0/query/01gd000002sztqjAAA-40000. Response content: [{u'errorCode': u'INVALID_QUERY_LOCATOR', u'message': u'Unable to find query cursor data for this QueryLocator......... and so on.........

Has anyone hit this before?
ecstar

Query Logically Deleted Records

Hi Love Simple Salesforce. Thank you for it.

Is there a way to query Logically deleted records? In other words can it do this:

SELECT Id FROM Contact WHERE isDeleted=true and masterRecordId != null
AND SystemModstamp > 2006-01-01T23:01:01+01:00

This query is what Salesforce suggests to know when two records have been merged.

While the simple-salesforce deleted() method returns a list of ids deleted, it does not give the masterRecordId.

The Salesforce restful api supports a 'query_all' to get logically deleted records. However simple-salesforce's query_all is a wrapper around query() and query_more()

Thank you,
David Link
New York

Get a List of Deleted Records Within a Given Timeframe

From salesforce api:

Use the SObject Get Deleted resource to get a list of deleted records for the specified object. Specify the date and time range
within which the records for the given object were deleted. Deleted records are written to a delete log (that is periodically
purged), and will be filtered out of most operations, such as SObject Rows or Query (although QueryAll will include deleted
records in results).

will send a pull request soon

Salesforce and TLSv1.1

Soon Salesforce will disable TLSv1.

A surprising number of people are still using versions of Python before 2.7.9, which is the first version of Python 2 to support TLSv1.1. This includes users of the most recent Ubuntu LTS, RHEL 7 and many other popular linux distros. These users will need to have PyOpenSSL and friends installed (with a system openssl library >= 1.0.1) so that the requests library simple_salesforce uses can speak TLSv1.1.

Could you include documentation for these users to at least give them a pointer about where to go if they have this problem? Are PRs on this topic welcome?

sf_version is always set to DEFAULT_API_VERSION

SalesforceLogin never checks to see if sf_version was passed as a parameter.
It always defaults to DEFAULT_API_VERSION. This prohibits invoking metadata APIs that became available after version '29.0'. The current version is '34.0'.

Update PyPi

Hi there,

Can you please upload the latest version to PyPi, it looks like the last update was 2014-08-29. A couple of nice new features I'd love to be able to use have made it into HEAD since then :)

Thanks!

[Ignore, i'm an idiot] Method to return sessionId

Any chance we could add a method to return the sessionId. Would like to use simple-salesforce in another app and its easier than writing my own session handler! :)

Ignore, found the SalesforceLogin method.

Possibility to respect a maximum request rate?

Our organisation has a very low request limit spread across a few scripts and programs that interface with Salesforce.

Is there a way to ensure that a script using this will remain under X requests per time interval?

This would allow me to spend less time handling this issue via manually waiting before each Salesforce request

Exception with Python 2.6

While using this module with Python 2.6 I'm getting the following exception:
File "/usr/lib64/python2.6/site-packages/requests/models.py", line 776, in json
return json.loads(self.text, *_kwargs)
File "/usr/lib64/python2.6/json/init.py", line 318, in loads
return cls(encoding=encoding, *_kw).decode(s)
TypeError: init() got an unexpected keyword argument 'object_pairs_hook'

the solution is to use simplejson
And change api.py import to
import simplejson as json

'Name or service not known' error when attempting to login.

Receive this error when attempting to login either with Salesforce.login() or SalesforceLogin()

This is when passing in user, password, token, and sandbox = True.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/site-packages/simple_salesforce/login.py", line 106, in SalesforceLogin
    proxies=kwargs.get('proxies', None))
  File "/usr/local/lib/python2.7/site-packages/requests/api.py", line 109, in post
    return request('post', url, data=data, json=json, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/api.py", line 50, in request
    response = session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 465, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 573, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/adapters.py", line 415, in send
    raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', gaierror(-2, 'Name or service not known'))

INVALID_LOGIN: Invalid username or password, locked out or Self-Service portal is not enabled

I am using the OrgAssessment tool from Babak Hosseinzadeh and I am receiving an INVALID_LOGIN failure when trying to login to Org62.

Below is the error message that is being provided when I run 'python SFDC_OrgAssess_Main.py'

Traceback (most recent call last):
  File "SFDC_OrgAssess_Main.py", line 64, in <module>
    org62 = Salesforce(username=Org62_user, password=Org62_pwd, organizationId='00D000000000062', version='35.0')
  File "/Users/mguse/anaconda/lib/python3.5/site-packages/simple_salesforce-0.68.1-py3.5.egg/simple_salesforce/api.py", line 108, in __init__
  File "/Users/mguse/anaconda/lib/python3.5/site-packages/simple_salesforce-0.68.1-py3.5.egg/simple_salesforce/login.py", line 129, in SalesforceLogin
simple_salesforce.login.SalesforceAuthenticationFailed: INVALID_LOGIN: Invalid username or password, locked out or Self-Service portal is not enabled

I am using version 0.68.1 of simple_salesforce in conjunction with the OrgAssessment tool.

I initially worked with Babak on resolving this issue, as it was working for him and other users, but we could not find any user mistake on my end (I double/triple-checked my user parameters and logged them as well). I then opened a ticket in Concierge for this, but they were not able to find a resolution for it and pointed me to the CORE SCM group in GUS. But I don't think that group could be of any help with this either.

Somehow I think the reference to "... Self-Service portal is not enabled" in the failure message might hint at something?

I am able to run the OrgAssessment tool just fine when commenting out the login code for Org62.

if pkg_resources.get_distribution('simple_salesforce').version.startswith("0.68.0"):
    sf = Salesforce(username=username, password=password, security_token=security_token, sandbox=sandbox, sf_version='35.0')
    org62 = Salesforce(username=Org62_user, password=Org62_pwd, organizationId='00D000000000062', sf_version='35.0')
else:
    sf = Salesforce(username=username, password=password, security_token=security_token, sandbox=sandbox, version='35.0')
    org62 = Salesforce(username=Org62_user, password=Org62_pwd, organizationId='00D000000000062', version='35.0')

That means the login to my customers org with using the security token is working ok, but when using the login into Org62 with use of the OrganizationId it fails for me.

Not sure where else to go at this point and hope this is something you might have come across in the past.

Upsert with external_id value containing "/" (forward slash) fails

I have a key field that is a url which fails on upsert because of the multiple forward slashes. Upserts in salesforce are done by url, replacing the id after the resource name, so the forward slashes break the url.

To reproduce the error:

Salesforce().Account.upsert("myfield__c/http://mysite.com/id", {})

.query returning no results.

Unsure if this is the right place for this, but I couldn't find any forums for Simple Salesforce.

I've been playing around with different functions to see what I have at my disposal before moving forward with my application. For reasons unknown to me, when I call the following (or any other format of SOQL with "Contact" in the FROM section of the query):

sf.query("SELECT Id, Email FROM Contact WHERE LastName = 'Jones'")

No results are returned, however running the same query directly from the salesforce developer console yields a multitude of results. Am I doing something wrong? I've been struggling with this for days and just can't figure it out.

When I query all IDs from "Organization", I yield only one result.

Thank you for any help.

Add the ability to define an existing requests.Session for Salesforce instance

I have a use case where I'm using a flow-based programming framework, where one of the workflow nodes executes a login. Because task nodes are intended to only have serializable data returned in order to support multiprocessing-based parallelization, I can return the session ID. In subsequent tasks that execute that are dependent on a Salesforce login, I can then take that session ID and pass it into a new salesforce instance with the "direct" instantiation. Unfortunately, this causes a unique requests.Session per instance.

What would be nice is if I could define the requests.Session instance in my own code external to the tasks and pass it into Salesforce.init to preserve the session for the duration of execution.

Support Custom URLs

Salesforce allows for URL customization but only standard instance domain/URL's are supported. There should be a way to specify the complete URL(s) on initialization.

Unable to serialize object Salesforce

I need to serialize object Salesforce so that I can store it in Django's request.session for repeated use. However, neither JSON- nor Pickle-based serializers can handle it. I ended up adding following code to Salesforce.__getattr__()

        # fix that enables pickle to work
        if name.startswith('__') and name.endswith('__'):
            return super(Salesforce, self).__getattr__(name)

to eliminate problems caused by special attributes, but there may be a better solution.

Error in generic rest function

Does anyone have an idea for the following error?

Traceback (most recent call last):
  File "./tests/unit/run_apex_tests.py", line 272, in <module>
    counts = run_tests()
  File "./tests/unit/run_apex_tests.py", line 228, in run_tests
    result = sf.restful('runTestsSynchronous', params={'classnames': classname})
  File "/usr/lib/python2.7/site-packages/simple_salesforce/api.py", line 216, in restful
    if len(json_result) == 0:
TypeError: object of type 'NoneType' has no len()

and this is the call I made:

result = sf.restful('runTestsSynchronous', params={'classnames': classname})

pip module versions with Python 2.7.5:

requests==2.7.0
simple-salesforce==0.67.2

UnicodeDecode Error

Introduced a UnicodeDecodeError in bb544ab#diff-62bde09fef12ecaa442a862253e09be3L104.

Maybe '{code}: {message}' should be u'{code}: {message}',

/home/ubuntu/.virtualenvs/jobsagg/local/lib/python2.7/site-packages/simple_salesforce/login.pyc in SalesforceLogin(**kwargs)
    113             response.content, 'sf:exceptionMessage')
    114         raise SalesforceAuthenticationFailed('{code}: {message}'.format(
--> 115             code=except_code, message=except_msg))
    116 
    117     session_id = getUniqueElementValueFromXmlString(response.content, 'sessionId')

UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 11: ordinal not in range(128)

Syntax error with python 3.2

Running this against python 3.2.3 I get the following error:

Traceback (most recent call last):
  File "test-api.py", line 12, in <module>
    from salesforce.client import SalesforceClient
  File "modules/salesforce/client.py", line 2, in <module>
    import simple_salesforce
  File "venv/lib/python3.2/site-packages/simple_salesforce/__init__.py", line 3, in <module>
    from simple_salesforce.api import (
  File "venv/lib/python3.2/site-packages/simple_salesforce/api.py", line 233
    search_string = u'FIND {{{search_string}}}'.format(search_string=search)
                                              ^
SyntaxError: invalid syntax

I don't think python 3.2 supports u'' for unicode literals, so perhaps that's the issue?

Require simplejson>=2.1.0 for Python 2.6

Per #87 Python 2.6 requires a version of simplejson that supports the object_pairs_hook keyword argument. That functionality was added in simplejson 2.1.0

We'll (probably) need to add to the if sys.version_info < (2, 7) test in setup.py pyver_install_requires.append('simplejson>=2.1.0')

Enhancement: Bulk API

Would it be possible to add a provision to use the Bulk API for queries, updates, inserts?

create() results in Exception - TypeError: 'dict' object is not callable

I am running the following code and getting a python error with everything I try ...

/simple-salesforce-0.66.2# python
Python 2.7.3 (default, Mar 14 2014, 11:57:14)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.

from simple_salesforce import Salesforce
SF_Username = "[email protected]"
SF_Password = "removed"
SF_SecurityToken = "removed"
sf = Salesforce(username=SF_Username, password=SF_Password, security_token=SF_SecurityToken)
sf.Contact.create({'LastName':'Smith','Email':'[email protected]'})
Traceback (most recent call last):
File "", line 1, in
File "simple_salesforce/api.py", line 437, in create
return result.json(object_pairs_hook=OrderedDict)
TypeError: 'dict' object is not callable

Token Usage

Not so much an issue, just a note. Not every org requires a token to access it, so this should be optional. The workaround is to just put some of your password in the token string :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.