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 registered SalesforceClient connection 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’t Id.

  • tooling — Set to True to 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 fields

  • IdField — Salesforce ID fields, with 15/18-character validation

  • NumberField — floating-point numeric fields

  • IntField — integer fields

  • CheckboxField — boolean fields

  • DateField — date fields (datetime.date)

  • DateTimeField — datetime fields (datetime.datetime)

  • TimeField — time fields (datetime.time)

  • PicklistField — picklist fields with allowed-value validation

  • MultiPicklistField — multi-select picklist fields (semicolon-delimited)

  • ReferenceField — lookup / master-detail relationship fields

  • ListField — child-relationship fields (subqueries)

  • BlobField — binary content fields; see Using BlobField for File Uploads

  • RawField — 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 null

  • FieldFlag.unique — field must be unique

  • FieldFlag.readonly — field cannot be assigned after initialization

  • FieldFlag.case_sensitive — text comparisons are case-sensitive

  • FieldFlag.updateable — field can be updated

  • FieldFlag.createable — field can be set on creation

  • FieldFlag.calculated — field is calculated by Salesforce (formula, etc.)

  • FieldFlag.filterable — field can be used in WHERE clauses

  • FieldFlag.sortable — field can be used in ORDER BY clauses

  • FieldFlag.groupable — field can be used in GROUP BY clauses

  • FieldFlag.permissionable — field-level security can be applied

  • FieldFlag.restricted_picklist — picklist values are restricted

  • FieldFlag.display_location_in_decimal — geolocation fields display in decimal

  • FieldFlag.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.