API Documentation

Configuration

Settings for Django-Select2.

class django_select2.conf.Select2Conf(**kwargs)[source]

Bases: AppConf

Settings for Django-Select2.

LIB_VERSION = '4.0.12'

Version of the Select2 library.

CACHE_BACKEND = 'default'

Django-Select2 uses Django’s cache to sure a consistent state across multiple machines.

Example of settings.py:

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
    'select2': {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/2",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

# Set the cache backend to select2
SELECT2_CACHE_BACKEND = 'select2'

Tip

To ensure a consistent state across all you machines you need to user a consistent external cache backend like Memcached, Redis or a database.

Note

Should you have copied the example configuration please make sure you have Redis setup. It’s recommended to run a separate Redis server in a production environment.

Note

The timeout of select2’s caching backend determines how long a browser session can last. Once widget is dropped from the cache the json response view will return a 404.

CACHE_PREFIX = 'select2_'

If you caching backend does not support multiple databases you can isolate select2 using the cache prefix setting. It has set select2_ as a default value, which you can change if needed.

JS = 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.12/js/select2.min.js'

The URI for the Select2 JS file. By default this points to the Cloudflare CDN.

If you want to select the version of the JS library used, or want to serve it from the local ‘static’ resources, add a line to your settings.py like so:

SELECT2_JS = 'assets/js/select2.min.js'

If you provide your own JS and would not like Django-Select2 to load any, change this setting to a blank string like so:

SELECT2_JS = ''

Tip

Change this setting to a local asset in your development environment to develop without an Internet connection.

CSS = 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.12/css/select2.min.css'

The URI for the Select2 CSS file. By default this points to the Cloudflare CDN.

If you want to select the version of the library used, or want to serve it from the local ‘static’ resources, add a line to your settings.py like so:

SELECT2_CSS = 'assets/css/select2.css'

If you want to add more css (usually used in select2 themes), add a line in settings.py like this:

SELECT2_CSS = [
    'assets/css/select2.css',
    'assets/css/select2-theme.css',
]

If you provide your own CSS and would not like Django-Select2 to load any, change this setting to a blank string like so:

SELECT2_CSS = ''

Tip

Change this setting to a local asset in your development environment to develop without an Internet connection.

THEME = 'default'

Select2 supports custom themes using the theme option so you can style Select2 to match the rest of your application.

Tip

When using other themes, you may need use select2 css and theme css.

I18N_PATH = 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.12/js/i18n'

The base URI for the Select2 i18n files. By default this points to the Cloudflare CDN.

If you want to select the version of the I18N library used, or want to serve it from the local ‘static’ resources, add a line to your settings.py like so:

SELECT2_I18N_PATH = 'assets/js/i18n'

Tip

Change this setting to a local asset in your development environment to develop without an Internet connection.

I18N_AVAILABLE_LANGUAGES = ['ar', 'az', 'bg', 'ca', 'cs', 'da', 'de', 'el', 'en', 'es', 'et', 'eu', 'fa', 'fi', 'fr', 'gl', 'he', 'hi', 'hr', 'hu', 'id', 'is', 'it', 'ja', 'km', 'ko', 'lt', 'lv', 'mk', 'ms', 'nb', 'nl', 'pl', 'pt-BR', 'pt', 'ro', 'ru', 'sk', 'sr-Cyrl', 'sr', 'sv', 'th', 'tr', 'uk', 'vi', 'zh-CN', 'zh-TW']

List of available translations.

List of ISO 639-1 language codes that are supported by Select2. If currently set language code (e.g. using the HTTP Accept-Language header) is in this list, Django-Select2 will use the language code to create load the proper translation.

The full path for the language file consists of:

from django.utils import translations

full_path = "{i18n_path}/{language_code}.js".format(
    i18n_path=settings.DJANGO_SELECT2_I18N,
    language_code=translations.get_language(),
)

settings.DJANGO_SELECT2_I18N refers to I18N_PATH.

JSON_ENCODER = 'django.core.serializers.json.DjangoJSONEncoder'

A JSONEncoder used to generate the API response for the model widgets.

A custom JSON encoder might be useful when your models uses a special primary key, that isn’t serializable by the default encoder.

class Meta[source]

Bases: object

Prefix for all Django-Select2 settings.

prefix = 'SELECT2'

Widgets

Django-Select2 Widgets.

These components are responsible for rendering the necessary HTML data markups. Since this whole package is to render choices using Select2 JavaScript library, hence these components are meant to be used with choice fields.

Widgets are generally of two types:

1. Light – They are not meant to be used when there are too many options, say, in thousands. This is because all those options would have to be pre-rendered onto the page and JavaScript would be used to search through them. Said that, they are also one the most easiest to use. They are a drop-in-replacement for Django’s default select widgets.

2(a). Heavy – They are suited for scenarios when the number of options are large and need complex queries (from maybe different sources) to get the options.

This dynamic fetching of options undoubtedly requires Ajax communication with the server. Django-Select2 includes a helper JS file which is included automatically, so you need not worry about writing any Ajax related JS code. Although on the server side you do need to create a view specifically to respond to the queries.

2(b). Model – Model-widgets are a further specialized versions of Heavies. These do not require views to serve Ajax requests. When they are instantiated, they register themselves with one central view which handles Ajax requests for them.

Heavy and Model widgets have respectively the word ‘Heavy’ and ‘Model’ in their name. Light widgets are normally named, i.e. there is no ‘Light’ word in their names.

digraph inheritancebe5871045b { bgcolor=transparent; rankdir=TB; size="8.0, 12.0"; "ChoiceWidget" [color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled]; "Widget" -> "ChoiceWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "HeavySelect2Mixin" [URL="#django_select2.forms.HeavySelect2Mixin",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Mixin that adds select2's AJAX options and registers itself on Django's cache."]; "HeavySelect2MultipleWidget" [URL="#django_select2.forms.HeavySelect2MultipleWidget",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Select2 multi select widget similar to :class:`.HeavySelect2Widget`."]; "HeavySelect2Mixin" -> "HeavySelect2MultipleWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "Select2MultipleWidget" -> "HeavySelect2MultipleWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "HeavySelect2TagWidget" [URL="#django_select2.forms.HeavySelect2TagWidget",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Select2 tag widget."]; "HeavySelect2Mixin" -> "HeavySelect2TagWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "Select2TagWidget" -> "HeavySelect2TagWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "HeavySelect2Widget" [URL="#django_select2.forms.HeavySelect2Widget",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Select2 widget with AJAX support that registers itself to Django's Cache."]; "HeavySelect2Mixin" -> "HeavySelect2Widget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "Select2Widget" -> "HeavySelect2Widget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "ModelSelect2Mixin" [URL="#django_select2.forms.ModelSelect2Mixin",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Widget mixin that provides attributes and methods for :class:`.AutoResponseView`."]; "ModelSelect2MultipleWidget" [URL="#django_select2.forms.ModelSelect2MultipleWidget",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Select2 drop in model multiple select widget."]; "ModelSelect2Mixin" -> "ModelSelect2MultipleWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "HeavySelect2MultipleWidget" -> "ModelSelect2MultipleWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "ModelSelect2TagWidget" [URL="#django_select2.forms.ModelSelect2TagWidget",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Select2 model widget with tag support."]; "ModelSelect2Mixin" -> "ModelSelect2TagWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "HeavySelect2TagWidget" -> "ModelSelect2TagWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "ModelSelect2Widget" [URL="#django_select2.forms.ModelSelect2Widget",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Select2 drop in model select widget."]; "ModelSelect2Mixin" -> "ModelSelect2Widget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "HeavySelect2Widget" -> "ModelSelect2Widget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "Select" [color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled]; "ChoiceWidget" -> "Select" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "Select2AdminMixin" [URL="#django_select2.forms.Select2AdminMixin",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Select2 mixin that uses Django's own select template."]; "Select2Mixin" [URL="#django_select2.forms.Select2Mixin",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="The base mixin of all Select2 widgets."]; "Select2MultipleWidget" [URL="#django_select2.forms.Select2MultipleWidget",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Select2 drop in widget for multiple select."]; "Select2Mixin" -> "Select2MultipleWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "SelectMultiple" -> "Select2MultipleWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "Select2TagMixin" [URL="#django_select2.forms.Select2TagMixin",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Mixin to add select2 tag functionality."]; "Select2TagWidget" [URL="#django_select2.forms.Select2TagWidget",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Select2 drop in widget for for tagging."]; "Select2TagMixin" -> "Select2TagWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "Select2Mixin" -> "Select2TagWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "SelectMultiple" -> "Select2TagWidget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "Select2Widget" [URL="#django_select2.forms.Select2Widget",color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled,target="_top",tooltip="Select2 drop in widget."]; "Select2Mixin" -> "Select2Widget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "Select" -> "Select2Widget" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "SelectMultiple" [color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled]; "Select" -> "SelectMultiple" [arrowsize=0.5,penwidth=0.75,style="setlinewidth(0.5)"]; "Widget" [color=gray30,fillcolor=gray90,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=14,height=0.25,shape=rect,style=filled]; }
class django_select2.forms.Select2Mixin(*args, **kwargs)[source]

Bases: object

The base mixin of all Select2 widgets.

This mixin is responsible for rendering the necessary data attributes for select2 as well as adding the static form media.

css_class_name = 'django-select2'
theme = None
empty_label = ''
build_attrs(base_attrs, extra_attrs=None)[source]

Add select2 data attributes.

optgroups(name, value, attrs=None)[source]

Add empty option for clearable selects.

property media

Construct Media as a dynamic property.

class django_select2.forms.Select2AdminMixin[source]

Bases: object

Select2 mixin that uses Django’s own select template.

css_class_name = 'admin-autocomplete'
theme = 'admin-autocomplete'
property media
class django_select2.forms.Select2TagMixin[source]

Bases: object

Mixin to add select2 tag functionality.

build_attrs(base_attrs, extra_attrs=None)[source]

Add select2’s tag attributes.

class django_select2.forms.Select2Widget(*args, **kwargs)[source]

Bases: Select2Mixin, Select

Select2 drop in widget.

Example usage:

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ('my_field', )
        widgets = {
            'my_field': Select2Widget
        }

or:

class MyForm(forms.Form):
    my_choice = forms.ChoiceField(widget=Select2Widget)
property media

Construct Media as a dynamic property.

class django_select2.forms.Select2MultipleWidget(*args, **kwargs)[source]

Bases: Select2Mixin, SelectMultiple

Select2 drop in widget for multiple select.

Works just like Select2Widget but for multi select.

property media

Construct Media as a dynamic property.

class django_select2.forms.Select2TagWidget(*args, **kwargs)[source]

Bases: Select2TagMixin, Select2Mixin, SelectMultiple

Select2 drop in widget for for tagging.

Example for django.contrib.postgres.fields.ArrayField:

class MyWidget(Select2TagWidget):

    def value_from_datadict(self, data, files, name):
        values = super().value_from_datadict(data, files, name)
        return ",".join(values)

    def optgroups(self, name, value, attrs=None):
        values = value[0].split(',') if value[0] else []
        selected = set(values)
        subgroup = [self.create_option(name, v, v, selected, i) for i, v in enumerate(values)]
        return [(None, subgroup, 0)]
property media

Construct Media as a dynamic property.

class django_select2.forms.HeavySelect2Mixin(attrs=None, choices=(), **kwargs)[source]

Bases: object

Mixin that adds select2’s AJAX options and registers itself on Django’s cache.

dependent_fields = {}
get_url()[source]

Return URL from instance or by reversing data_view.

build_attrs(base_attrs, extra_attrs=None)[source]

Set select2’s AJAX attributes.

render(*args, **kwargs)[source]

Render widget and register it in Django’s cache.

set_to_cache()[source]

Add widget object to Django’s cache.

You may need to overwrite this method, to pickle all information that is required to serve your JSON response view.

class django_select2.forms.HeavySelect2Widget(attrs=None, choices=(), **kwargs)[source]

Bases: HeavySelect2Mixin, Select2Widget

Select2 widget with AJAX support that registers itself to Django’s Cache.

Usage example:

class MyWidget(HeavySelect2Widget):
    data_view = 'my_view_name'

or:

class MyForm(forms.Form):
    my_field = forms.ChoiceField(
        widget=HeavySelect2Widget(
            data_url='/url/to/json/response'
        )
    )
property media

Construct Media as a dynamic property.

class django_select2.forms.HeavySelect2MultipleWidget(attrs=None, choices=(), **kwargs)[source]

Bases: HeavySelect2Mixin, Select2MultipleWidget

Select2 multi select widget similar to HeavySelect2Widget.

property media

Construct Media as a dynamic property.

class django_select2.forms.HeavySelect2TagWidget(attrs=None, choices=(), **kwargs)[source]

Bases: HeavySelect2Mixin, Select2TagWidget

Select2 tag widget.

property media

Construct Media as a dynamic property.

class django_select2.forms.ModelSelect2Mixin(*args, **kwargs)[source]

Bases: object

Widget mixin that provides attributes and methods for AutoResponseView.

property empty_label
model = None
queryset = None
search_fields = []

Model lookups that are used to filter the QuerySet.

Example:

search_fields = [
        'title__icontains',
    ]
max_results = 25

Maximal results returned by AutoResponseView.

set_to_cache()[source]

Add widget’s attributes to Django’s cache.

Split the QuerySet, to not pickle the result set.

filter_queryset(request, term, queryset=None, **dependent_fields)[source]

Return QuerySet filtered by search_fields matching the passed term.

Parameters:
  • request (django.http.request.HttpRequest) – The request is being passed from the JSON view and can be used to dynamically alter the response queryset.

  • term (str) – Search term

  • queryset (django.db.models.query.QuerySet) – QuerySet to select choices from.

  • **dependent_fields – Dependent fields and their values. If you want to inherit from ModelSelect2Mixin and later call to this method, be sure to pop everything from keyword arguments that is not a dependent field.

Returns:

Filtered QuerySet

Return type:

QuerySet

get_queryset()[source]

Return QuerySet based on queryset or model.

Returns:

QuerySet of available choices.

Return type:

QuerySet

get_search_fields()[source]

Return list of lookup names.

optgroups(name, value, attrs=None)[source]

Return only selected options and set QuerySet from ModelChoicesIterator.

label_from_instance(obj)[source]

Return option label representation from instance.

Can be overridden to change the representation of each choice.

Example usage:

class MyWidget(ModelSelect2Widget):
    def label_from_instance(obj):
        return str(obj.title).upper()
Parameters:

obj (django.db.models.Model) – Instance of Django Model.

Returns:

Option label.

Return type:

str

class django_select2.forms.ModelSelect2Widget(*args, **kwargs)[source]

Bases: ModelSelect2Mixin, HeavySelect2Widget

Select2 drop in model select widget.

Example usage:

class MyWidget(ModelSelect2Widget):
    search_fields = [
        'title__icontains',
    ]

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ('my_field', )
        widgets = {
            'my_field': MyWidget,
        }

or:

class MyForm(forms.Form):
    my_choice = forms.ChoiceField(
        widget=ModelSelect2Widget(
            model=MyOtherModel,
            search_fields=['title__icontains']
        )
    )

Tip

The ModelSelect2(Multiple)Widget will try to get the QuerySet from the fields choices. Therefore you don’t need to define a QuerySet, if you just drop in the widget for a ForeignKey field.

property media

Construct Media as a dynamic property.

class django_select2.forms.ModelSelect2MultipleWidget(*args, **kwargs)[source]

Bases: ModelSelect2Mixin, HeavySelect2MultipleWidget

Select2 drop in model multiple select widget.

Works just like ModelSelect2Widget but for multi select.

property media

Construct Media as a dynamic property.

class django_select2.forms.ModelSelect2TagWidget(*args, **kwargs)[source]

Bases: ModelSelect2Mixin, HeavySelect2TagWidget

Select2 model widget with tag support.

This it not a simple drop in widget. It requires to implement you own value_from_datadict() that adds missing tags to you QuerySet.

Example:

class MyModelSelect2TagWidget(ModelSelect2TagWidget):
    queryset = MyModel.objects.all()

    def value_from_datadict(self, data, files, name):
        '''Create objects for given non-pimary-key values. Return list of all primary keys.'''
        values = set(super().value_from_datadict(data, files, name))
        # This may only work for MyModel, if MyModel has title field.
        # You need to implement this method yourself, to ensure proper object creation.
        pks = self.queryset.filter(**{'pk__in': list(values)}).values_list('pk', flat=True)
        pks = set(map(str, pks))
        cleaned_values = list(values)
        for val in values - pks:
            cleaned_values.append(self.queryset.create(title=val).pk)
        return cleaned_values
property media

Construct Media as a dynamic property.

URLs

Django-Select2 URL configuration.

Add django_select to your urlconf if you use any ‘Model’ fields:

from django.urls import path


path('select2/', include('django_select2.urls')),

Views

JSONResponse views for model widgets.

class django_select2.views.AutoResponseView(**kwargs)[source]

Bases: BaseListView

View that handles requests from heavy model widgets.

The view only supports HTTP’s GET method.

get(request, *args, **kwargs)[source]

Return a django.http.JsonResponse.

Example:

{
    'results': [
        {
            'text': "foo",
            'id': 123
        }
    ],
    'more': true
}
get_queryset()[source]

Get QuerySet from cached widget.

get_paginate_by(queryset)[source]

Paginate response by size of widget’s max_results parameter.

get_widget_or_404()[source]

Get and return widget from cache.

Raises:

Http404 – If if the widget can not be found or no id is provided.

Returns:

Widget from cache.

Return type:

ModelSelect2Mixin

Cache

Shared memory across multiple machines to the heavy AJAX lookups.

Select2 uses django.core.cache to share fields across multiple threads and even machines.

Select2 uses the cache backend defined in the setting SELECT2_CACHE_BACKEND [default=``default``].

It is advised to always setup a separate cache server for Select2.

JavaScript

DjangoSelect2 handles the initialization of select2 fields automatically. Just include {{ form.media.js }} in your template before the closing body tag. That’s it!

If you insert forms after page load or if you want to handle the initialization yourself, DjangoSelect2 provides a jQuery plugin, replacing and enhancing the Select2 plugin. It will handle both normal and heavy fields. Simply call djangoSelect2(options) on your select fields.:

$('.django-select2').djangoSelect2();

You can pass see Select2 options if needed:

$('.django-select2').djangoSelect2({placeholder: 'Select an option'});

Please replace all your .select2 invocations with the here provided .djangoSelect2.

Security & Authentication

Security is important. Therefore make sure to read and understand what the security measures in place and their limitations.

Set up a separate cache. If you have a public form that uses a model widget make sure to setup a separate cache database for Select2. An attacker could constantly reload your site and fill up the select2 cache. Having a separate cache allows you to limit the effect to select2 only.

You might want to add a secure select2 JSON endpoint for data you don’t want to be accessible to the general public. Doing so is easy:

class UserSelect2View(LoginRequiredMixin, AutoResponseView):
    pass

class UserSelect2WidgetMixin(object):
    def __init__(self, *args, **kwargs):
        kwargs['data_view'] = 'user-select2-view'
        super(UserSelect2WidgetMixin, self).__init__(*args, **kwargs)

class MySecretWidget(UserSelect2WidgetMixin, ModelSelect2Widget):
    model = MySecretModel
    search_fields = ['title__icontains']