function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
chicchic 

salesforce integration using Python

I am new to salesforce.com and python. Is anyone able to do integration with slasesforce.com using Python? I am mainly interested in nightly batch job process but any examples or general advice is highly appreciated. One big issue is that we are in a very old version of Python, v2.1.3. I'd like to hear from you and your sucess stories or nightmares if you have done any integration with salesforce.com using python. Thank you very much.

Best Regards

Elaine
bangusbangus
I'm at the early stages of development and I am integrating SF with mailman. I am using SOAPpy only to get to SF. Right now have I have working scripts to login and do batch updates of Contacts and Oppotunities. I'll post scripts if you need them.
chicchic

I would love to take a look of your scripts if you can share it.  What version of Python do you use?

Thanks

adamgadamg
There is a complete (and from what I understand pretty cool!) python toolkit for sforce here - http://sourceforge.net/project/showfiles.php?group_id=96634&package_id=134423

..of course, unless you fished around the sforce sourceforge project you wouldn't know that We'll work on getting that promoted better.
surfoussurfous
Elaine,

My org is one of the largest users of python to integrate with the sforce API. We wrote the python toolkit that Adam pointed you to on the sforce sourceforge site and have done a great deal of development adn integration with it; it's served us well.

That's not to say that it's without limitations. Some serious hacking had to be done with the underlying SOAP library, ZSI (http://pywebsvcs.sourceforge.net) to get it to work. As a result, we've taken it up to the 4.0 API, but cannot go more recent with it without diving back into the guts again.

What we're looking at now (and which shows much more promise) is using a different python SOAP library, SOAPpy. It's also found at http://pywebsvcs.sourceforge.net. It's fairly easy to install following the instructions there. We use python 2.3.4 and the latest released version of SOAPpy, 11.5.

Next, you want to look for a message on these boards titled, 'Example of using Python & SOAPpy to invoke sfroce methods using Partner.WSDL,' by Max B. This little snippet of code will show you how to use the Partner WSDL to login and make calls against the sforce API. Once you get this test code working, you have complete (as far as I can tell) access to the sforce API as described in the API docs.

I'm working on building a small core that more tightly integrates the sforce API with python itself, but that's got some work to go yet. In the meantime, just having the full API access from python should get you going.

--Kevin
bangusbangus
i saw that sourceforce project, ZSI and was too complicated for us. I just used SOAPpy with functions limited to what I needed - query Account, Contact, add Opportunity, and update Contact. All these from enterprise.wdsl.

3 py files below, sftest.py is the test

sfinit.py
==========================================================================

# wrapper to SF login
# usage:
# server = SFConnect(wsdlfile).connect(user, passw)
# server is the stub
from SOAPpy import WSDL

urn = "urn:enterprise.soap.sforce.com"
class SFConnect:
class __S(WSDL.Proxy):
def setDebug(self, cond = 1):
from SOAPpy import Config
self.soapproxy.config.dumpSOAPOut = cond
self.soapproxy.config.dumpSOAPIn = 0
if cond == 2:
self.soapproxy.config.dumpSOAPIn = 1

def __init__(self, wsdl, dumpwsdl = False): # this throws
WSDL.Config.strictNamespaces = 0
WSDL.Config.BuildWithNoType =1
self.server = SFConnect.__S(wsdl) #WSDL.Proxy(wsdl)
if dumpwsdl:
self.__wsdldump()

# self.server.setDebug(2)

# This is from someone in the sforce forum - his comments

# For some reason WSDLTools does not assign "urn:partner.soap.sforce.com"
# namespace to operations when it parses binding
for method in self.server.methods.itervalues():
method.namespace = urn


def connect(self, user, pw): # this throws
lr = self.server.login(username=user, password=pw)
# This is from someone in the sforce forum - his comments
for method in self.server.methods.itervalues():
method.location = lr.serverUrl
# Assigning returned sessionId to SOAP header
#
# SOAP Header support in SOAPpy does not allow us to types declared
# in WSDL, so it has to be done manually
# The idea is from http://sourceforge.net/mailarchive/forum.php?thread_id=4990257&forum_id=1729
from SOAPpy import structType
from SOAPpy import headerType
ct = structType(data = {"sessionId" : lr.sessionId})
#Ditch the standard namespaces
ct._validURIs = []
#Set the one we want to use
ct._ns = ("ns1", urn)
hd = headerType(data = {"SessionHeader" : ct})
self.server.soapproxy.header = hd
return self.server
sffun.py
==========================================================================
# utility functions to support mailman integration with SF

import string
import datetime
from SOAPpy import structType
from SOAPpy import dateTimeType
class SFFunException(Exception):
pass
class SFFunExceptionNoRecover(Exception): # caller cannot recover
pass
class OpportunityUpdateFields:
# fields to be updated in opportunity table
def __init__(self):
#
# Notes: required fields are Name, CloseDate, Stage
self.OwnerId = None
self.keySaleElement = None #### ????
self.AccountId = None
self.Amount = None
self.Type = None
self.CloseDate = None
self.StageName = None
self.Description = None

self.Name = None
self.BillingStartDate = None #### ????
self.BillingContact = None #### ????
self.ContactRole = None ####????

def as_dict(self):
m = {}
def set(k, v):
if v: m[k] = v

set("Name", self.Name)
set("StageName", self.StageName)
set("CloseDate", dateTimeType(data = self.CloseDate)) #data=[2005,6,22, 0, 0,0])
set("OwnerId", self.OwnerId)
set("AccountId", self.AccountId)
set("Amount", self.Amount)
set("Type", self.Type)
set("Description", self.Description)

# have no clue where the marketing guys got these
set("keySaleElement", self.keySaleElement)
set("BillingStartDate", self.BillingStartDate)
set("BillingContact", self.BillingContact)
set("ContactRole", self.ContactRole)

return m


class FPNOpportunity(OpportunityUpdateFields):
def __init__(self, sffun, email, name, description, amount):
OpportunityUpdateFields.__init__(self)
self.OwnerId = sffun.getuserid()
self.Description = description
self.Amount = 0
self.StageName = "Sale Complete"
self.Amount = amount
self.Name = name
try:
self.AccountId = sffun.findContact(email)[0]["AccountId"]
except IndexError:
sffun.logger.error("NO ACCOUNTID")
except KeyError:
sffun.logger.error("NO ACCOUNTID")
self.Type = "FPN Professional"



class SFFun:
def __init__(self, server, logger):
self.server = server
self.logger = logger
self.userinfo = self.server.getUserInfo()
logger.debug("USERID: %s"%self.userinfo.userId)

def getuserid(self):
return self.userinfo.userId

def __genquery(self, fieldset, object, condition):
ls = []

qu = "select %s from %s"%(string.join(fieldset, ","), object)
if condition:
qu = qu + " where %s"%condition
self.logger.debug(qu)
r = self.server.query(queryString=qu)

self.logger.debug("RSET SIZE: %d"%int(r.size))

if r.size > 0:
if int(r.size) == 1:
m = {}
for f in fieldset:
try:
m[f] = r.records[f]
except KeyError:
self.logger.warn("FIELD %s is not in response"%f)
ls.append(m)
else:
for rec in r.records:
# ls.append((rec.FirstName, rec.LastName))
m = {}
d = dir(rec)
for f in fieldset:
if f in d:
m[f] = rec[f]
else:
self.logger.warn("FIELD %s is not in response"%f)
ls.append(m)


return ls



def findContact(self, email):
''' returns [(first,last), ...] if found
'''
cond = "Email = '%s'"%email
return self.__genquery(["FirstName", "LastName", "AccountId"], "Contact", cond)

def __make_sObject(self, m, objtype):
at = ["xsi:type", objtype]
ct = structType(data=m, name="sObjects", typed=0, attrs=at)
return ct

def __doUpdate(self, obName, key, rset, newvalues):
### FIXME
### updates one record at a time
### i.e. ONE SOAP transaction per update !!!!
count = 0
for rs in rset:
try:
accid = rs[key]
m = newvalues
self.logger.debug("updating %s with %s"%(accid, str(m)))
m[key] = accid
lr = self.server.update(self.__make_sObject(m, obName))
self.logger.debug("update result "+str(lr.success))
if not lr.success:
self.logger.error("UPDATE FAILED")
else:
count = count + 1
except KeyError:
if rs.has_key(reqfld):
self.logger.error("NO AccountId for %s"%rs[reqfld])
else:
self.logger.error("NO AccountId")
return count

def __doCreate(self, obName, newvalues):
self.server.setDebug(2)
self.logger.debug("creating %s"%(obName))
lr = self.server.create(self.__make_sObject(newvalues.as_dict(), obName))
self.logger.debug("create result "+str(lr.success))
if not lr.success:
self.logger.error("CREATE FAILED")
self.server.setDebug(0)


def updateContact(self, condition, newvalues): # will raise
object = "Contact"
key = "Id"#"AccountId"
reqfld = "LastName" # known required field
res = self.__genquery([key, reqfld], object, condition)
if not len(res):
logger.error("record to update not found")
return 0

self.logger.debug("No of records to update: %d"%len(res))
self.logger.debug(str(res))

return self.__doUpdate(object, key, res, newvalues)


def getAllContacts(self):
return self.dumpAllFields("Opportunity")

def addOpportunity(self, op):
self.__doCreate("Opportunity", op)



def dumpAllFields(self, obj):
flds = []
res = self.server.describeSObject(obj)
self.logger.debug("FIELD LIST")
for k in res.fields:
self.logger.debug(k.name)
flds.append(k.name)
return self.__genquery(flds, obj, None)

def getAllOppurtunities(self):
return self.dumpAllFields("Opportunity")

sftest.py
==========================================================================
import sys
from sfinit import SFConnect

u = "u@me.com"
p = "maggiedog"
s = SFConnect("./enterprise.wsdl").connect(u, p)
# Requesting sforce user info for the user logged in

from sffun import SFFun


fn = SFFun(s, getStdLogger("test"))

from sffun import FPNOpportunity
fn.addOpportunity(FPNOpportunity(fn, "rose@edge.com", "Put name here", "Put description here", 1234.56))
chicchic

Thanks.  Can you send me the files because the postings loss all the indentations..

Elaine

chicchic

I tried your codes, after making minor changes to SOAPpy 0.11.1 to make it works in Python 2.1.3, I got the following error:

addr = https://www.salesforce.com/services/Soap/c/6.0
data = <?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:login xmlns:ns1="urn:enterprise.soap.sforce.com" SOAP-ENC:root="1">
<password xsi:type="xsd:string">marine23</password>
<username xsi:type="xsd:string">fgarcia@homegain.com</username>
</ns1:login>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

 namespace = urn:enterprise.soap.sforce.com
soapaction =
encoding = UTF-8
http_proxy = None
config = <SOAPpy.Config.SOAPConfig instance at 0x814bce4>
befor sending payload, payload:
   r = <httplib.HTTPS instance at 0x8c6377c>
   data = <?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:login xmlns:ns1="urn:enterprise.soap.sforce.com" SOAP-ENC:root="1">
<password xsi:type="xsd:string">marine23</password>
<username xsi:type="xsd:string">fgarcia@homegain.com</username>
</ns1:login>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

code= 500
msg= Internal Server Error
headers= Server: Resin/3.0.s040331
Content-Type: text/xml; charset=utf-8
Connection: close
Date: Fri, 24 Jun 2005 21:49:10 GMT

content-type= text/xml; charset=utf-8
data=
*** Incoming HTTP headers **********************************************
HTTP/1.? 500 Internal Server Error
Server: Resin/3.0.s040331
Content-Type: text/xml; charset=utf-8
Connection: close
Date: Fri, 24 Jun 2005 21:49:10 GMT
************************************************************************
Traceback (most recent call last):
  File "sftest.py", line 10, in ?
    s = SFConnect("./enterprise.wsdl").connect(u, p)
  File "sfinit.py", line 39, in connect
    lr = self.server.login(username=user, password=pw)
  File "/home/soft/lib/python2.1/site-packages/SOAPpy/Client.py", line 421, in __call__
    return self.__r_call(*args, **kw)
  File "/home/soft/lib/python2.1/site-packages/SOAPpy/Client.py", line 443, in __r_call
    self.__hd, self.__ma)
  File "/home/soft/lib/python2.1/site-packages/SOAPpy/Client.py", line 345, in __call
    config = self.config)
  File "/home/soft/lib/python2.1/site-packages/SOAPpy/Client.py", line 242, in call
    raise HTTPError(code, msg)
SOAPpy.Errors.HTTPError: <HTTPError 500 Internal Server Error>

I thought about this might be a problem with the wsdl, which I do need to change all tns:Header to Header as posted in another message by someone to make it loaded.  However, when I make a simple call to google, which has much simpler wsdl, and I get the same 500 error.  It leads me to think it might be something in the SOAPpy.  Anyone has any suggestion where I can look into the problem?  Thank you very much.

Best Regards

Elaine

Max BMax B
Elaine,

I have tried my sample code against your account with enterprise.wsdl that I generated off my dev account. Everyting worked fine. I was using Python 2.4 and SOAPpy 0.12.0

By the way, I found out that you don't need the following hack if you are using 0.12.0
#Ditch the standard namespaces
ct._validURIs = []
#Set the one we want to use
ct._ns = ("ns1", "urn:partner.soap.sforce.com")

Anyway, I have an idea why it fails: it's the blank soapaction. In you dump you have:
namespace = urn:enterprise.soap.sforce.com
soapaction =
encoding = UTF-8
http_proxy = None

where I have:
*** Outgoing HTTP headers **********************************************
POST /services/Soap/c/6.0 HTTP/1.0
Host: www.salesforce.com
User-agent: SOAPpy 0.12.0 (http://pywebsvcs.sf.net)
Content-type: text/xml; charset="UTF-8"
Content-length: 610
SOAPAction: "login"
************************************************************************

I believe this throws web server off when it tries to find which method to invoke.

Best regards,
Max

P.S. I belive wsdl loads fine with or without "tns:". I tried, it made no difference.
chicchic

Thanks Max.

I go through the wsdl (both partner and enterprise), and I realize that both files generated don't have any soapaction name in any of the operations.  For example, here is my partner.wsdl for login

<operation name="login">
            <soap:operation soapAction="login"/>
            <input>

                <soap:header use="literal" message="Header" part="CallOptions"/>

                <soap:body parts="parameters" use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
            <fault name="LoginFault">
               <soap:fault name="LoginFault" use="literal"/>
            </fault>
            <fault name="UnexpectedErrorFault">
               <soap:fault name="UnexpectedErrorFault" use="literal"/>
            </fault>
        </operation>

When I put in the "login" soapaction name, my dump shows it but it still doesn't work.  I start to believe the version of SOAPpy (v0.11.0) doesn't work at all.  I did able to make a call sucessfully using another example posted which manually populate the SOAP header and envelope, and doesn't use either ZSI or SOAPpy.  This is really not what I want to do but I don't seem to have any choice for now.

Best Regards

Elaine

Rahul Sharma 580Rahul Sharma 580
Simple Salesforce is a basic Salesforce.com REST API client built for Python 2.6, 2.7, 3.3, 3.4, 3.5, and 3.6. 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  (https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_what_is_rest_api.htm)
I have practiced on the same project in my python course (https://www.inventateq.com/python-training-course-bangalore.php) with some errors and the above document helped me a lot to learn and rectify errors.
 
Rajakumar NRajakumar N
What version of Python do you use? If you can share those details it will be helpful. https://www.aimoretechnologies.com/training-courses/python-training