Skip to content

Custom Fields

Custom fields allow you to extend CRM models with user-defined attributes. Field definitions are stored separately from the data, and values are validated automatically on create and update.

CustomFieldDefinition

Defines a custom field that can be added to a CRM model.

Field Type Default Description
entity_type See below required Which model this field applies to
field_name str required Field identifier (unique per entity type)
field_label str required Human-readable label
field_type Literal['text', 'number', 'date', 'choice'] required Value type
choices list[str] \| None None Allowed values (for choice type)
is_required bool False Whether the field is required
default_value Any \| None None Default value
help_text str \| None None Help text for UI display
display_order int 0 Display order in UI

There is a unique constraint on (entity_type, field_name).

Supported Entity Types

Custom fields can be defined for these models:

  • Entity
  • EntityRelationship
  • Deal
  • EntityIdentifier
  • EntityContactPoint
  • EntityAddress

Example

from amsdal_crm.models.custom_field_definition import CustomFieldDefinition

# Define a text field for entities
CustomFieldDefinition(
    entity_type='Entity',
    field_name='industry',
    field_label='Industry',
    field_type='text',
    is_required=False,
    help_text='Primary industry sector',
    display_order=1,
).save(force_insert=True)

# Define a choice field for deals
CustomFieldDefinition(
    entity_type='Deal',
    field_name='deal_source',
    field_label='Deal Source',
    field_type='choice',
    choices=['Inbound', 'Outbound', 'Referral', 'Partner'],
    is_required=True,
    display_order=2,
).save(force_insert=True)

CustomFieldsMixin

The CustomFieldsMixin adds a custom_fields dict to models and hooks into the create/update lifecycle for automatic validation.

Models that include this mixin: Entity, EntityRelationship, Deal, EntityIdentifier, EntityContactPoint, EntityAddress.

Setting Custom Field Values

from amsdal_crm.models.entity import Entity

entity = Entity(
    name='Acme Corp',
    custom_fields={
        'industry': 'Technology',
    },
)
entity.save(force_insert=True)  # custom_fields validated via pre_create hook

How Validation Works

On every pre_create and pre_update, the mixin calls CustomFieldService.validate_custom_fields() which:

  1. Loads all CustomFieldDefinition records for the model's entity type
  2. Rejects any field names not defined in definitions (CustomFieldValidationError)
  3. Checks required fields are present and not None
  4. Validates and converts values by type

Type Validation

Field Type Validation Conversion
text Accepts any value Converts to str
number Must be a valid number Converts to Decimal
date Must be a valid ISO date Parses and re-serializes as ISO format
choice Must be in the choices list No conversion

Invalid values raise CustomFieldValidationError:

from amsdal_crm.errors import CustomFieldValidationError

Configuration

The maximum number of custom field definitions per entity type is controlled by:

AMSDAL_CRM_MAX_CUSTOM_FIELDS_PER_ENTITY=50  # default