Working with SObjects
SObject is the base class for Salesforce object models. It provides a Pythonic interface for working with Salesforce records, mapping field types to native Python types and exposing module-level CRUD helpers in sf_toolkit.io.
Defining SObjects
Create a class that inherits from SObject and define fields. The api_name keyword argument controls which Salesforce SObject this class maps to (when omitted, the class name is used):
from sf_toolkit import SObject
from sf_toolkit.data.fields import IdField, TextField, NumberField, FieldFlag
class Account(SObject, api_name="Account"):
Id = IdField()
Name = TextField()
AnnualRevenue = NumberField()
Industry = TextField()
Rating = TextField()
Class-level options
SObject subclasses accept several keyword arguments at class-definition time:
api_name— Salesforce API name for this object (defaults to the class name).connection— Name of a registeredSalesforceClientconnection to use by default for this SObject. When omitted, the default connection is used.id_field— Field name to treat as the record’s identifier (defaults to"Id"). Useful for objects whose primary identifier isn’tId.tooling— Set toTrueto use the Tooling API endpoints instead of the standard Data API.
class CustomToolingObject(
SObject,
api_name="CustomObject__c",
connection="my_org",
tooling=True,
):
Id = IdField()
Name = TextField()
Field Types
Salesforce Toolkit provides field types that map to Salesforce field types and convert values to/from native Python types:
TextField— string, text, and (untyped) picklist fieldsIdField— Salesforce ID fields, with 15/18-character validationNumberField— floating-point numeric fieldsIntField— integer fieldsCheckboxField— boolean fieldsDateField— date fields (datetime.date)DateTimeField— datetime fields (datetime.datetime)TimeField— time fields (datetime.time)PicklistField— picklist fields with allowed-value validationMultiPicklistField— multi-select picklist fields (semicolon-delimited)ReferenceField— lookup / master-detail relationship fieldsListField— child-relationship fields (subqueries)BlobField— binary content fields; see Using BlobField for File UploadsRawField— passthrough field that performs no validation or conversion
Picklist option lists are passed using the options keyword:
from sf_toolkit.data.fields import PicklistField, MultiPicklistField
class Account(SObject, api_name="Account"):
Rating = PicklistField(options=["Hot", "Warm", "Cold"])
Categories__c = MultiPicklistField(options=["A", "B", "C"])
Field Flags
Field flags are passed positionally to a field constructor and describe its behavior:
FieldFlag.nillable— field can be nullFieldFlag.unique— field must be uniqueFieldFlag.readonly— field cannot be assigned after initializationFieldFlag.case_sensitive— text comparisons are case-sensitiveFieldFlag.updateable— field can be updatedFieldFlag.createable— field can be set on creationFieldFlag.calculated— field is calculated by Salesforce (formula, etc.)FieldFlag.filterable— field can be used in WHERE clausesFieldFlag.sortable— field can be used in ORDER BY clausesFieldFlag.groupable— field can be used in GROUP BY clausesFieldFlag.permissionable— field-level security can be appliedFieldFlag.restricted_picklist— picklist values are restrictedFieldFlag.display_location_in_decimal— geolocation fields display in decimalFieldFlag.write_requires_master_read— master-detail field requires master read
from sf_toolkit.data.fields import DateTimeField, FieldFlag
class Account(SObject, api_name="Account"):
CreatedDate = DateTimeField(FieldFlag.readonly)
Default Field Values
You can specify a default value for a field so that new SObject instances automatically receive a value when one is not provided in the constructor.
Passing a static default:
from sf_toolkit.data.fields import (
TextField, CheckboxField, PicklistField, MultiPicklistField,
)
class Account(SObject, api_name="Account"):
Name = TextField()
Active__c = CheckboxField(default=True)
Rating = PicklistField(options=["Hot", "Warm", "Cold"], default="Cold")
Categories__c = MultiPicklistField(
options=["A", "B", "C"], default=["A", "C"]
)
# Active__c, Rating, Categories__c are filled automatically
account = Account(Name="Example")
assert account.Active__c is True
assert account.Rating == "Cold"
assert set(account.Categories__c.values) == {"A", "C"}
Using a callable default (evaluated per instance):
from datetime import date, datetime
from sf_toolkit.data.fields import DateField, DateTimeField, PicklistField
class Task(SObject, api_name="Task"):
DueDate__c = DateField(default=date.today) # called for each new instance
CreatedMarker__c = DateTimeField(default=datetime.utcnow)
Priority__c = PicklistField(
options=["High", "Normal", "Low"], default="Normal"
)
task = Task(Subject="Follow Up")
# If you do not pass DueDate__c, it is set to today's date.
Notes:
The default is only applied if the field is not supplied when constructing the instance.
Callables (functions taking no arguments) are supported and invoked once per instance creation.
Defaults must pass field validation (e.g., picklist values must be in the allowed set).
CRUD Operations
CRUD helpers are exposed as module-level functions in sf_toolkit.io. Each function takes the SObject instance (or class plus ID) and resolves the Salesforce client either from an explicit sf_client= argument, the SObject class’s connection attribute, or the default connection.
Each operation has an async counterpart with the suffix _async; see Asynchronous Operations.
Creating Records
from sf_toolkit.io import save_insert
account = Account(
Name="Test Account",
Industry="Technology",
Rating="Hot",
)
# Insert into Salesforce; the new Id is set on the record
save_insert(account)
print(account.Id)
Reading Records
from sf_toolkit.io import fetch, fetch_list
from sf_toolkit.data.sobject import SObjectList
# Retrieve by ID
account: Account = fetch(Account, "001xxxxxxxxxxxxxxx")
# Fetch many records by ID using the composite API
accounts: SObjectList[Account] = fetch_list(
Account, "001xxxxxxxxxxxxxxx", "001yyyyyyyyyyyyyyy"
)
To re-pull a record’s current values from Salesforce, use reload():
from sf_toolkit.io.api import reload
reload(account)
Updating Records
from sf_toolkit.io import fetch, save_update
account = fetch(Account, "001xxxxxxxxxxxxxxx")
account.Name = "Updated Name"
account.Rating = "Warm"
# Update in Salesforce
save_update(account)
# Send only the modified fields
save_update(account, only_changes=True)
Deleting Records
from sf_toolkit.io import delete, fetch
account = fetch(Account, "001xxxxxxxxxxxxxxx")
delete(account)
Upsert with External ID
from sf_toolkit.io import save_upsert
account = Account(
ExternalId__c="EXT123",
Name="New Account",
)
# Upsert based on external ID
save_upsert(account, external_id_field="ExternalId__c")
Generic save
save() automatically chooses between save_insert, save_update, and save_upsert based on the state of the record and the arguments passed:
from sf_toolkit.io import save
# No Id set -> insert
save(Account(Name="New"))
# Id set -> update
account.Name = "Updated"
save(account)
# external_id_field provided -> upsert
save(account, external_id_field="ExternalId__c")
Working with SObjectList
SObjectList is a typed list of SObject records that ships with bulk-friendly helpers. Pass it to one of the list helpers in sf_toolkit.io to operate on many records using Salesforce’s composite API:
from sf_toolkit.data.sobject import SObjectList
from sf_toolkit.io import (
save_list,
save_insert_list,
save_update_list,
save_upsert_list,
delete_list,
)
accounts = SObjectList(
Account(Name=f"Bulk Account {i}") for i in range(100)
)
# Generic save (insert/update/upsert per record)
results = save_list(accounts)
# Operation-specific helpers
save_insert_list(accounts, batch_size=200)
save_update_list(accounts, only_changes=True)
save_upsert_list(accounts, external_id_field="ExternalId__c")
delete_list(accounts)
For datasets that exceed the composite-API limits (typically 10k+ records), see Bulk API.
Dynamic SObject Creation
You can also create SObject classes dynamically from Salesforce describe metadata using sobject_from_description():
from sf_toolkit.io import sobject_from_description
# Generate an SObject class from describe metadata
Contact = sobject_from_description("Contact")
# Use the dynamically created class like any other SObject
contact = Contact(FirstName="John", LastName="Doe")
save_insert(contact)
You can narrow the generated class to a subset of fields with the include_fields or ignore_fields arguments, or pass a custom base_class to add behavior to the generated type.
Blob (File) Fields
For SObjects that hold file content (ContentVersion, Document, Attachment), define a BlobField. SF Toolkit handles the multipart upload automatically when you call save_insert / save_update / save. See Using BlobField for File Uploads for details.