Configuration

This module provides configuration classes for customizing TomSelect widgets and their plugins. The configuration system is based on dataclasses, providing type safety and validation.

Core Configuration

TomSelectConfig

class django_tomselect.app_settings.TomSelectConfig(url='autocomplete', show_list=False, show_create=False, show_detail=False, show_update=False, show_delete=False, value_field='id', label_field='name', create_field='', filter_by=<factory>, exclude_by=<factory>, use_htmx=False, attrs=<factory>, close_after_select=None, hide_placeholder=None, highlight=True, hide_selected=True, load_throttle=300, loading_class='loading', max_items=None, max_options=None, open_on_focus=True, placeholder='Select a value', preload='focus', create=False, create_filter=None, create_with_htmx=False, minimum_query_length=2, css_framework='default', use_minified=False, plugin_checkbox_options=None, plugin_clear_button=None, plugin_dropdown_header=None, plugin_dropdown_footer=None, plugin_dropdown_input=None, plugin_remove_button=None)[source]

Bases: BaseConfig

Main configuration class for TomSelect widgets, supplied as a config argument to the form field.

This class contains settings specific to a particular TomSelect widget.

There are two distinct mechanisms for creating new items:

  1. Dropdown Footer Link (show_create): Adds a “Create New” link in the dropdown footer that navigates to a separate create view. Requires all three of: show_create=True, plugin_dropdown_footer=PluginDropdownFooter(), and create_url set on the autocomplete view. The link is only shown if the user has the model’s add permission.

  2. Inline Creation (create): Enables Tom Select’s native type-to-create feature. When the user types a value that doesn’t match any existing option, an “Add <value>…” option appears in the dropdown. Use create_field to specify which model field receives the typed value. Optionally set create_with_htmx=True to POST the new value to the autocomplete view’s create_url via HTMX instead of handling it client-side.

Both mechanisms can be used together. See the “Creating New Items” section in the docs for complete examples.

Parameters:
  • url (str) – URL name for the autocomplete view.

  • show_list (bool) – if True, show the “View All” link in the dropdown footer. Requires plugin_dropdown_footer and list_url on the autocomplete view.

  • show_create (bool) – if True, show a “Create New” link in the dropdown footer that navigates to the autocomplete view’s create_url. Requires plugin_dropdown_footer to be configured and create_url to be set on the autocomplete view. The link is only visible to users with the model’s add permission.

  • show_detail (bool) – if True, show a detail link for each selected option. Requires detail_url on the autocomplete view.

  • show_update (bool) – if True, show an update link for each selected option. Requires update_url on the autocomplete view.

  • show_delete (bool) – if True, show a delete link for each selected option. Requires delete_url on the autocomplete view.

  • value_field (str) – model field name used as the option value (default: “id”).

  • label_field (str) – model field name used as the option display label (default: “name”).

  • create_field (str) – model field name to populate when creating a new item via inline creation (create=True). For example, create_field='name' means the typed value will be used as the name field of the new object.

  • filter_by (tuple | FilterSpec | list) – Filter conditions to apply. Accepts: - Empty tuple () for no filtering (default) - 2-tuple (“field”, “lookup”) for legacy single field filter - FilterSpec object for a single condition - List of FilterSpec or 2-tuples for multiple conditions - Use Const(“value”, “lookup”) for constant filters.

  • exclude_by (tuple | FilterSpec | list) – Exclude conditions to apply. Same format as filter_by.

  • use_htmx (bool) – if True, use HTMX for AJAX requests.

  • attrs (dict[str, str]) – additional HTML attributes for the widget.

  • close_after_select (bool | None) – if True, close the dropdown after selecting an item.

  • hide_placeholder (bool | None) – if True, hide the placeholder when an item is selected.

  • highlight (bool) – if True, highlight the matching text in the dropdown.

  • hide_selected (bool) – if True, hide the selected item in the dropdown.

  • load_throttle (int) – throttle time in milliseconds for loading items.

  • loading_class (str) – CSS class for the loading indicator.

  • max_items (int | None) – maximum number of items to display in the dropdown.

  • max_options (int | None) – maximum number of options to display in the dropdown.

  • open_on_focus (bool) – if True, open the dropdown when the input is focused.

  • placeholder (str | Promise | None) – placeholder text for the input field.

  • preload (Literal['focus'] | bool) – if True, preload the dropdown on focus.

  • create (bool) – if True, enable Tom Select’s inline item creation. When the user types a value that doesn’t match any existing option, an “Add <value>…” option appears in the dropdown. This is independent of show_create, which controls the dropdown footer link. Use create_field to specify which model field receives the typed value.

  • create_filter (str | None) – regex pattern or function to restrict which typed values can trigger inline creation. Only applies when create=True.

  • create_with_htmx (bool) – if True, inline creation (create=True) POSTs to the autocomplete view’s create_url via HTMX for server-side validation and object creation. Requires create=True and create_url on the autocomplete view. If False, creation is handled client-side by Tom Select.

  • minimum_query_length (int) – minimum number of characters to trigger a search.

  • css_framework (AllowedCSSFrameworks) – CSS framework to use (“default”, “bootstrap4”, “bootstrap5”).

  • use_minified (bool) – if True, use minified JS and CSS files.

  • plugin_checkbox_options (PluginCheckboxOptions | None) – PluginCheckboxOptions instance.

  • plugin_clear_button (PluginClearButton | None) – PluginClearButton instance.

  • plugin_dropdown_header (PluginDropdownHeader | None) – PluginDropdownHeader instance.

  • plugin_dropdown_footer (PluginDropdownFooter | None) – PluginDropdownFooter instance. Required for show_create and show_list to render their respective links in the dropdown.

  • plugin_dropdown_input (PluginDropdownInput | None) – PluginDropdownInput instance.

  • plugin_remove_button (PluginRemoveButton | None) – PluginRemoveButton instance.

__init__(url='autocomplete', show_list=False, show_create=False, show_detail=False, show_update=False, show_delete=False, value_field='id', label_field='name', create_field='', filter_by=<factory>, exclude_by=<factory>, use_htmx=False, attrs=<factory>, close_after_select=None, hide_placeholder=None, highlight=True, hide_selected=True, load_throttle=300, loading_class='loading', max_items=None, max_options=None, open_on_focus=True, placeholder='Select a value', preload='focus', create=False, create_filter=None, create_with_htmx=False, minimum_query_length=2, css_framework='default', use_minified=False, plugin_checkbox_options=None, plugin_clear_button=None, plugin_dropdown_header=None, plugin_dropdown_footer=None, plugin_dropdown_input=None, plugin_remove_button=None)
Parameters:
Return type:

None

as_dict()[source]

Convert config to dictionary for template rendering.

Return type:

dict

get_normalized_excludes()[source]

Get exclude_by as a normalized list of FilterSpec objects.

Return type:

list[FilterSpec]

get_normalized_filters()[source]

Get filter_by as a normalized list of FilterSpec objects.

Return type:

list[FilterSpec]

update(**kwargs)[source]

Return a new config with updated values.

Note: Since TomSelectConfig is a frozen dataclass, this method returns a new instance with the specified fields updated rather than modifying in place.

Return type:

TomSelectConfig

validate()[source]

Validate the complete configuration.

Return type:

None

verify_config_types()[source]

Verify that the configuration types are correct.

Raises:

TypeError – If any plugin configuration has an invalid type.

Returns:

True if all configurations are valid.

Return type:

bool

The TomSelectConfig class is the main configuration class for TomSelect widgets. It controls both the widget’s behavior and which plugins are enabled.

            graph TD
        A[Project Settings] -->|Override| B[GLOBAL_DEFAULT_CONFIG]
        C[Field Config] -->|Override| D[Widget Config]
        B -->|Base| D

        D --> E{Config Type}
        E -->|Model| F[TomSelectModelWidget]
        E -->|Iterables| G[TomSelectIterablesWidget]

        F --> H[Build Context]
        G --> H

        H --> I[Render Template]

        subgraph Plugins
            J[Checkbox Options]
            K[Clear Button]
            L[Dropdown Header]
            M[Dropdown Footer]
            N[Dropdown Input]
            O[Remove Button]
        end

        D -.->|Configure| Plugins
    

Basic Usage

from django_tomselect.app_settings import TomSelectConfig

config = TomSelectConfig(
    url='book-autocomplete',
    value_field='id',
    label_field='title',
    placeholder='Select a book...',
    minimum_query_length=2
)

Complete Configuration Example

from django_tomselect.app_settings import TomSelectConfig, Const

config = TomSelectConfig(
    # Core Settings
    url='book-autocomplete',
    value_field='id',
    label_field='name',
    create_field='',

    # Filtering - supports multiple formats (see "Dependent Fields" section below)
    filter_by=('category', 'category_id'),  # Simple: filter by category form field
    # Or for multiple filters:
    # filter_by=[
    #     ('category', 'category_id'),
    #     Const('published', 'status'),  # Always filter to published status
    # ],
    exclude_by=(),  # No excludes (default)
    use_htmx=False,  # Enable HTMX integration
    attrs={},  # Additional HTML attributes

    # Display Settings
    placeholder='Select a value',
    minimum_query_length=2,
    max_items=None,
    max_options=None,

    # Behavior Settings
    preload='focus',  # Can be 'focus', True, or False
    highlight=True,
    open_on_focus=True,
    close_after_select=None,
    hide_selected=True,
    hide_placeholder=None,
    create=False,  # Enable item creation
    create_filter=None,  # Filter for new items
    create_with_htmx=False,  # Use HTMX for item creation

    # Performance Settings
    load_throttle=300,
    loading_class='loading',

    # Feature Toggles (all default to False)
    show_list=False,
    show_create=False,
    show_detail=False,
    show_update=False,
    show_delete=False,

    # Framework Settings
    css_framework='bootstrap5',
    use_minified=True,

    # Plugins
    plugin_checkbox_options=PluginCheckboxOptions(),
    plugin_clear_button=PluginClearButton(),
    plugin_dropdown_header=PluginDropdownHeader(),
    plugin_dropdown_footer=PluginDropdownFooter(),
    plugin_dropdown_input=PluginDropdownInput(),
    plugin_remove_button=PluginRemoveButton()
)

Filter Specification Classes

FilterSpec

class django_tomselect.app_settings.FilterSpec(source, lookup, source_type='field', levels_up=0)[source]

Bases: object

Specification for a single filter/exclude condition.

This class represents a single filter or exclude condition that can be applied to an autocomplete queryset. It supports both field-based filtering (where the value comes from another form field) and constant filtering (where the value is a static constant).

Parameters:
  • source (str)

  • lookup (str)

  • source_type (Literal['field', 'const'])

  • levels_up (int)

source

Form field name (for field-based filters) OR constant value (for const filters).

Type:

str

lookup

Django ORM lookup field (e.g., “category_id”, “status”).

Type:

str

source_type

Either “field” (value from form field) or “const” (static value).

Type:

Literal[‘field’, ‘const’]

levels_up

Number of formset levels to walk up from the select to find the source field. 0 (default) means the source is at the same nesting depth as the select. 1 means the source lives in the parent formset row. Only valid for source_type=”field”.

Type:

int

Example

# Field-based filter (filter by value from another form field) FilterSpec(source=”category”, lookup=”category_id”, source_type=”field”)

# Constant filter (always filter to a specific value) FilterSpec(source=”published”, lookup=”status”, source_type=”const”)

# Cross-level filter: inner row references outer order’s customer FilterSpec(source=”customer”, lookup=”id”, source_type=”field”, levels_up=1)

__init__(source, lookup, source_type='field', levels_up=0)
Parameters:
  • source (str)

  • lookup (str)

  • source_type (Literal['field', 'const'])

  • levels_up (int)

Return type:

None

__post_init__()[source]

Validate levels_up type and value, and its compatibility with source_type.

classmethod from_tuple(t)[source]

Create a FilterSpec from a legacy 2-tuple format.

Parameters:

t (tuple[str, str]) – A tuple of (field_name, lookup_field).

Returns:

A FilterSpec with source_type=”field”.

Return type:

FilterSpec

The FilterSpec dataclass represents a single filter or exclude condition. It supports both field-based filtering (where the value comes from another form field) and constant filtering (where the value is a static constant).

from django_tomselect.app_settings import FilterSpec

# Field-based filter (value from form field)
spec = FilterSpec(source='category', lookup='category_id', source_type='field')

# Constant filter (static value)
spec = FilterSpec(source='published', lookup='status', source_type='const')

Fields:

Field

Type

Default

Description

source

str

required

Form field name (for source_type='field') or constant value (for source_type='const').

lookup

str

required

Django ORM lookup applied on the autocomplete side (e.g. category_id, status).

source_type

'field' | 'const'

'field'

Whether source names a form field or holds a literal value.

levels_up

int

0

Number of formset levels to walk up from the select to find the source field. Only valid for source_type='field'. See Cross-level filters in nested formsets.

Cross-level filters in nested formsets

For nested formsets (a formset inside a parent formset row), levels_up lets an inner row pull its filter value from a field on an outer ancestor row. levels_up=0 (the default) is the original behavior: the source field lives in the same row as the select. levels_up=1 walks up one formset segment, levels_up=2 walks up two, and so on.

# Inner "line items" formset row filters Products by the parent Order's customer
FilterSpec(source='customer', lookup='id', source_type='field', levels_up=1)

For a select with id id_orders-2-line_items-3-product, this resolves the source field to id_orders-2-customer rather than id_orders-2-line_items-3-customer.

Validation:

  • levels_up must be a non-negative int. bool, float, and str are rejected.

  • levels_up != 0 is rejected when source_type='const' (there’s no field to walk up to).

  • Walking past the outermost formset is a no-op and falls back to the original prefix.

Const Helper

django_tomselect.app_settings.Const(value, lookup)[source]

Helper to create constant filter specs.

Creates a FilterSpec that always filters by a constant value rather than getting the value from another form field.

Parameters:
  • value – The constant value to filter by. Scalars are stringified. A list or tuple is comma-joined into a single source string for use with list-valued lookups such as __in or __range - items must not contain literal commas.

  • lookup (str) – The Django ORM lookup field.

Returns:

A FilterSpec with source_type=”const”.

Raises:

ValidationError – If value is an empty list/tuple, or if any item in a list/tuple value contains a literal comma.

Return type:

FilterSpec

Example

# Always filter to only published items Const(“published”, “status”)

# Filter by a specific category ID Const(“5”, “category_id”)

# Filter by multiple IDs via __in Const([11, 13], “id__in”)

The Const function is a convenience helper for creating constant filter specs:

from django_tomselect.app_settings import Const

# These are equivalent:
Const('published', 'status')
FilterSpec(source='published', lookup='status', source_type='const')

Const also accepts a list or tuple of values for use with list-valued lookups such as __in and __range. Items are stringified and comma-joined into a single source string; the autocomplete view splits the value back into a list before calling filter()/exclude(). Items must not contain literal commas (an empty list is also rejected).

# Restrict the queryset to specific primary keys
Const([11, 13], 'id__in')

# Equivalent to qs.filter(year__range=[2020, 2024])
Const([2020, 2024], 'year__range')

Plugin Configurations

PluginCheckboxOptions

class django_tomselect.app_settings.PluginCheckboxOptions[source]

Bases: BaseConfig

Plugin configuration for the checkbox_options plugin.

No additional settings are required. If this plugin is enabled, the widget will display checkboxes.

__init__()
Return type:

None

Enables checkbox selection in the dropdown. No additional configuration required.

config = TomSelectConfig(
    plugin_checkbox_options=PluginCheckboxOptions()
)

PluginClearButton

class django_tomselect.app_settings.PluginClearButton(title='Clear Selections', class_name='clear-button')[source]

Bases: BaseConfig

Plugin configuration for the clear_button plugin.

Parameters:
  • title (str | Promise)

  • class_name (str)

__init__(title='Clear Selections', class_name='clear-button')
Parameters:
  • title (str | Promise)

  • class_name (str)

Return type:

None

Adds a button to clear all selections.

config = TomSelectConfig(
    plugin_clear_button=PluginClearButton(
        title="Clear Selections",
        class_name="clear-button",
    )
)

PluginDropdownHeader

class django_tomselect.app_settings.PluginDropdownHeader(title='Autocomplete', header_class='container-fluid bg-primary text-bg-primary pt-1 pb-1 mb-2 dropdown-header', title_row_class='row', label_class='form-label', value_field_label='Value', label_field_label='Label', label_col_class='col-6', show_value_field=False, extra_columns=<factory>)[source]

Bases: BaseConfig

Plugin configuration for the dropdown_header plugin.

Parameters:
  • title (str | Promise)

  • header_class (str)

  • title_row_class (str)

  • label_class (str)

  • value_field_label (str | Promise)

  • label_field_label (str | Promise)

  • label_col_class (str)

  • show_value_field (bool)

  • extra_columns (dict[str, str])

__init__(title='Autocomplete', header_class='container-fluid bg-primary text-bg-primary pt-1 pb-1 mb-2 dropdown-header', title_row_class='row', label_class='form-label', value_field_label='Value', label_field_label='Label', label_col_class='col-6', show_value_field=False, extra_columns=<factory>)
Parameters:
  • title (str | Promise)

  • header_class (str)

  • title_row_class (str)

  • label_class (str)

  • value_field_label (str | Promise)

  • label_field_label (str | Promise)

  • label_col_class (str)

  • show_value_field (bool)

  • extra_columns (dict[str, str])

Return type:

None

as_dict()[source]

Return the configuration as a dictionary with evaluated translations.

validate()[source]

Validate dropdown header config.

Return type:

None

Configures the dropdown header display.

config = TomSelectConfig(
    plugin_dropdown_header=PluginDropdownHeader(
        title="Autocomplete",
        header_class="container-fluid bg-primary text-bg-primary pt-1 pb-1 mb-2 dropdown-header",
        title_row_class="row",
        label_class="form-label",
        value_field_label="Value",
        label_field_label="Label",
        label_col_class="col-6",
        show_value_field=False,
        extra_columns={
            'author__name': 'Author',
            'publication_year': 'Year',
        }
    )
)

PluginDropdownFooter

class django_tomselect.app_settings.PluginDropdownFooter(title='Autocomplete Footer', footer_class='container-fluid mt-1 px-2 border-top dropdown-footer', list_view_label='List View', list_view_class='btn btn-primary btn-sm m-2 p-1 float-end float-right', create_view_label='Create New', create_view_class='btn btn-primary btn-sm m-2 p-1 float-end float-right')[source]

Bases: BaseConfig

Plugin configuration for the dropdown_footer plugin.

Parameters:
  • title (str | Promise) – title for the footer.

  • footer_class (str) – CSS class for the footer container.

  • list_view_label (str | Promise)

  • list_view_class (str)

  • create_view_label (str | Promise)

  • create_view_class (str)

__init__(title='Autocomplete Footer', footer_class='container-fluid mt-1 px-2 border-top dropdown-footer', list_view_label='List View', list_view_class='btn btn-primary btn-sm m-2 p-1 float-end float-right', create_view_label='Create New', create_view_class='btn btn-primary btn-sm m-2 p-1 float-end float-right')
Parameters:
  • title (str | Promise)

  • footer_class (str)

  • list_view_label (str | Promise)

  • list_view_class (str)

  • create_view_label (str | Promise)

  • create_view_class (str)

Return type:

None

Configures the dropdown footer display.

config = TomSelectConfig(
    plugin_dropdown_footer=PluginDropdownFooter(
        title="Autocomplete Footer",
        footer_class="container-fluid mt-1 px-2 border-top dropdown-footer",
        list_view_label="List View",
        list_view_class="btn btn-primary btn-sm m-2 p-1 float-end float-right",
        create_view_label="Create New",
        create_view_class="btn btn-primary btn-sm m-2 p-1 float-end float-right",
    )
)

PluginDropdownInput

class django_tomselect.app_settings.PluginDropdownInput[source]

Bases: BaseConfig

Plugin configuration for the dropdown_input plugin.

No additional settings are required. If this plugin is enabled, the widget will display an input field.

__init__()
Return type:

None

Enables an input field in the dropdown. No additional configuration required.

config = TomSelectConfig(
    plugin_dropdown_input=PluginDropdownInput()
)

PluginRemoveButton

class django_tomselect.app_settings.PluginRemoveButton(title='Remove this item', label='&times;', class_name='remove')[source]

Bases: BaseConfig

Plugin configuration for the remove_button plugin, which removed an item from the list of selected items.

Parameters:
  • title (str | Promise) – title for the remove button.

  • label (str) – label for the remove button.

  • class_name (str) – CSS class for the remove button.

__init__(title='Remove this item', label='&times;', class_name='remove')
Parameters:
  • title (str | Promise)

  • label (str)

  • class_name (str)

Return type:

None

Adds a remove button to selected items.

config = TomSelectConfig(
    plugin_remove_button=PluginRemoveButton(
        title="Remove this item",
        label="&times;",
        class_name="remove",
    )
)

Global Configuration

You can set global defaults in your Django settings:

# settings.py

TOMSELECT = {
    # Default CSS framework for all widgets
    'DEFAULT_CSS_FRAMEWORK': 'bootstrap5',

    # Use minified files by default
    'DEFAULT_USE_MINIFIED': True,

    # Default configuration for all widgets
    'DEFAULT_CONFIG': {
        'minimum_query_length': 2,
        'load_throttle': 300,
        'preload': 'focus'
    },

    # Default plugin configurations
    'PLUGINS': {
        'checkbox_options': True,
        'clear_button': {
            'title': 'Clear Selection',
            'class_name': 'clear-btn'
        },
        'dropdown_header': {
            'title': 'Select an Option',
            'show_value_field': False
        },
        'dropdown_footer': {
            'title': 'Options',
            'footer_class': 'footer'
        },
        'remove_button': {
            'title': 'Remove',
            'label': '×'
        }
    },

    # Logging configuration
    'ENABLE_LOGGING': True,  # Set to False to disable logging

    # Custom proxy request class
    'PROXY_REQUEST_CLASS': 'path.to.CustomProxyRequest',

    # Permission cache settings (optional)
    'PERMISSION_CACHE': {
        'TIMEOUT': 3600,  # Cache timeout in seconds
        'KEY_PREFIX': 'myapp',  # Prefix for cache keys
        'NAMESPACE': 'tomselect'  # Namespace for cache keys
    },

    # Custom JSON encoder for autocomplete responses (optional)
    'DEFAULT_JSON_ENCODER': 'path.to.CustomJSONEncoder',
}

Advanced Usage

Dependent Fields (filter_by / exclude_by)

The filter_by and exclude_by options allow you to create dependent fields that filter based on other form field values or constant values.

Basic Usage (Legacy Format)

The simplest format is a 2-tuple of (form_field_name, lookup_field):

config = TomSelectConfig(
    filter_by=('category', 'category_id'),  # Filter where category_id = value of 'category' form field
    exclude_by=('author', 'author_id')      # Exclude where author_id = value of 'author' form field
)

Multiple Field Filters

You can filter by multiple fields using a list of tuples. All conditions are combined (AND):

config = TomSelectConfig(
    filter_by=[
        ('magazine', 'magazine_id'),  # Filter by selected magazine
        ('status', 'status'),         # AND by selected status
    ]
)

Constant Value Filters

Use the Const helper to filter by a constant value that doesn’t come from a form field:

from django_tomselect.app_settings import TomSelectConfig, Const

config = TomSelectConfig(
    filter_by=[
        ('magazine', 'magazine_id'),       # Filter by selected magazine
        Const('published', 'status'),      # Always filter to published only
    ]
)

This is useful for:

  • Always showing only active/published items

  • Filtering by the current user’s organization

  • Enforcing business rules in the UI

For list-valued lookups, pass a list or tuple as the value:

config = TomSelectConfig(
    filter_by=[
        Const([11, 13], 'id__in'),         # Restrict to a specific set of IDs
        Const([2020, 2024], 'year__range'),  # Restrict year to a range
    ]
)

FilterSpec Objects

For advanced use cases, you can use FilterSpec objects directly:

from django_tomselect.app_settings import TomSelectConfig, FilterSpec

config = TomSelectConfig(
    filter_by=[
        FilterSpec(source='category', lookup='category_id', source_type='field'),
        FilterSpec(source='active', lookup='is_active', source_type='const'),
    ]
)

Accepted Formats Summary

Format

Example

Description

Empty tuple

()

No filtering (default)

2-tuple of strings

('field', 'lookup')

Single field filter (legacy form)

FilterSpec

FilterSpec(...)

Single spec object

Const

Const('value', 'lookup')

Constant value filter

Const with list

Const([11, 13], 'id__in')

Constant filter for __in/__range lookups

List or tuple of specs

[('f1', 'l1'), Const(...)] or (spec1, spec2)

Multiple filters (AND logic)

URL Parameter Format

When using multiple filters, the autocomplete URL will include multiple f (filter) or e (exclude) parameters:

  • Field filters: ?f='fieldname__lookup=value'

  • Constant filters: ?f='__const__lookup=value'

  • Multiple: ?f='...'&f='...'&f='...'

Creating New Items

Django TomSelect provides two different mechanisms for creating new items:

  1. Dropdown Footer Link (show_create) - Adds a “Create New” link in the dropdown footer that navigates to a create view

  2. Inline Creation (create) - Enables Tom Select’s native feature where users can type a new value and create it directly

Understanding the Parameters

Parameter

Location

Purpose

show_create

TomSelectConfig

Shows a “Create New” link in the dropdown footer (requires PluginDropdownFooter)

create

TomSelectConfig

Enables Tom Select’s inline item creation when typing non-matching values

create_field

TomSelectConfig

Specifies which field receives the new value when creating inline

create_with_htmx

TomSelectConfig

When True, inline creation POSTs to a URL via HTMX instead of client-side handling

create_url

Autocomplete view

URL name for the create view (used by both dropdown footer link and HTMX creation)

Inline Item Creation

Tom Select’s native create feature allows users to type a value that doesn’t exist and create it:

config = TomSelectConfig(
    url='tag-autocomplete',
    create=True,           # Enable inline creation
    create_field='name',   # Field to populate with the typed value
)

HTMX-Based Inline Creation

For server-side validation and creation, use HTMX:

from django_tomselect.app_settings import TomSelectConfig

class ArticleForm(forms.Form):
    category = TomSelectModelChoiceField(
        config=TomSelectConfig(
            url='category-autocomplete',
            create=True,              # Enable inline creation UI
            create_field='name',      # Field for the new value
            create_with_htmx=True,    # POST to server via HTMX
        )
    )

When create_with_htmx=True, clicking the create option will POST to the create_url defined on your autocomplete view. Your autocomplete view must have create_url set:

class CategoryAutocompleteView(AutocompleteModelView):
    model = Category
    search_lookups = ['name__icontains']
    create_url = 'category-create'  # Required for HTMX creation

Note

The HTMX creation feature requires:

  1. create=True in the config

  2. create_with_htmx=True in the config

  3. create_url defined on the autocomplete view

  4. User must have add permission for the model

Complete Example

Here’s a complete example combining both dropdown footer and inline HTMX creation:

# autocompletes.py
class CategoryAutocompleteView(AutocompleteModelView):
    model = Category
    search_lookups = ['name__icontains']
    create_url = 'category-create'
    list_url = 'category-list'

# forms.py
class ArticleForm(forms.Form):
    category = TomSelectModelChoiceField(
        config=TomSelectConfig(
            url='category-autocomplete',
            # Dropdown footer link
            show_create=True,
            plugin_dropdown_footer=PluginDropdownFooter(
                create_view_label="Open Create Form",
            ),
            # Inline HTMX creation
            create=True,
            create_field='name',
            create_with_htmx=True,
        )
    )

# urls.py
urlpatterns = [
    path('autocomplete/category/', CategoryAutocompleteView.as_view(), name='category-autocomplete'),
    path('category/create/', CategoryCreateView.as_view(), name='category-create'),
    path('category/', CategoryListView.as_view(), name='category-list'),
]

Logging

By default, logging is enabled. To disable logging or configure per-module log levels, see the Logging section in Utilities.

Quick disable:

TOMSELECT = {
    "ENABLE_LOGGING": False
}

Proxy Request

To automate the interaction between a widget and its associated autocomplete view, we must pass a request. Normally this is very straightforward, but in some cases, you may need to pass a request that has been modified to include additional information. In these cases, you can subclass django_tomselect.request.DefaultProxyRequest and override the __init__ method to add the necessary data.

from django_tomselect.request import DefaultProxyRequest

class CustomProxyRequest(DefaultProxyRequest):
    def __init__(self, request, extra_data):
        super().__init__(request)
        self.extra_data = extra_data

Then, use the custom proxy request in your configuration:

TOMSELECT = {
    # Other settings...

    # Custom proxy request
    'PROXY_REQUEST_CLASS': 'path.to.CustomProxyRequest',
}

Custom JSON Encoder

If your models contain fields with non-serializable types (e.g.: using PhoneNumber from django-phonenumber-field, custom objects, etc), you can specify a custom JSON encoder to handle serialization in autocomplete responses.

Global Configuration

Set a default JSON encoder for all autocomplete views:

# settings.py
import json

class CustomJSONEncoder(json.JSONEncoder):
    """Custom encoder that handles otherwise non-serializable types."""

    def default(self, obj):
        # Handle PhoneNumber objects
        if hasattr(obj, 'as_e164'):
            return obj.as_e164
        # Handle other custom types
        if hasattr(obj, '__str__'):
            return str(obj)
        return super().default(obj)

TOMSELECT = {
    # Other settings...

    # Custom JSON encoder (can be a class or dotted string path)
    'DEFAULT_JSON_ENCODER': CustomJSONEncoder,
    # Or as a string:
    # 'DEFAULT_JSON_ENCODER': 'myapp.encoders.CustomJSONEncoder',
}

Per-View Configuration

You can also set a custom encoder on individual autocomplete views, which takes precedence over global setting:

from django_tomselect.autocompletes import AutocompleteModelView
from myapp.encoders import CustomJSONEncoder

class ContactAutocomplete(AutocompleteModelView):
    model = Contact
    search_lookups = ['name__icontains', 'phone__icontains']
    value_fields = ['id', 'name', 'phone']

    # Custom JSON encoder for this view
    json_encoder = CustomJSONEncoder
    # Or as dotted string path:
    # json_encoder = 'myapp.encoders.CustomJSONEncoder'

Precedence

The JSON encoder is resolved in this order:

  1. View-level json_encoder attribute (if set)

  2. Global DEFAULT_JSON_ENCODER setting

  3. Django’s default DjangoJSONEncoder (when neither is set)

Custom Validation

Add custom validation to configuration:

from django.core.exceptions import ValidationError
from django_tomselect.app_settings import TomSelectConfig

class CustomConfig(TomSelectConfig):
    def validate(self):
        super().validate()
        # Add custom validation logic
        if self.placeholder and len(self.placeholder) > 100:
            raise ValidationError("Placeholder text is too long")
        if self.show_create and not self.create_field:
            raise ValidationError("create_field must be set when show_create is enabled")

Dynamic Configuration

Create configuration based on runtime conditions:

def get_config(user):
    return TomSelectConfig(
        show_create=user.is_staff,
        show_delete=user.is_superuser,
        max_items=10 if user.is_premium else 5,
        plugin_dropdown_header=PluginDropdownHeader(
            title="Select" if user.is_anonymous else f"Welcome {user.username}"
        )
    )