Skip to main content

Django autocomplete widgets and views using Tom Select

Project description

Tom Select for Django

Django autocomplete widgets and views using Tom Select.

This package provides a Django autocomplete widget and view that can be used together to provide a user interface for selecting a model instance from a database table.

The package is adapted from the fantastic work of Philip Becker in mizdb-tomselect, with the goal of a more generalized solution for Django autocompletion.


Installation

Install:

pip install -U django-tomselect

Usage

Add to installed apps:

INSTALLED_APPS = [
    # ...
    "django_tomselect"
]

Configure an endpoint for autocomplete requests:

# urls.py
from django.urls import path

from django_tomselect.views import AutocompleteView

urlpatterns = [
    # ...
    path("autocomplete/", AutocompleteView.as_view(), name="my_autocomplete_view")
]

Use the widgets in a form.

from django import forms

from django_tomselect.widgets import TomSelectWidget, TomSelectTabularWidget
from .models import City, Person


class MyForm(forms.Form):
    city = forms.ModelChoiceField(
        City.objects.all(),
        widget=TomSelectWidget(City, url="my_autocomplete_view"),
    )

    # Display results in a table, with additional columns for fields
    # "first_name" and "last_name":
    person = forms.ModelChoiceField(
        Person.objects.all(),
        widget=TomSelectTabularWidget(
            Person,
            url="my_autocomplete_view",
            search_lookups=[
                "full_name__icontains",
            ],
            # for extra columns pass a mapping of {"model_field": "Column Header Label"}
            extra_columns={"first_name": "First Name", "last_name": "Last Name"},
            # The column header label for the labelField column
            label_field_label="Full Name",
        ),
    )

NOTE: Make sure to include bootstrap somewhere. For example in the template:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Django Tom Select Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
    {{ form.media }}
</head>
<body>
<div class="container">
    <form>
        {% csrf_token %}
        {{ form.as_div }}
        <button type="submit" class="btn btn-success">Save</button>
    </form>
</div>
</body>
</html>

Widgets

The widgets pass attributes necessary to make autocomplete requests to the HTML element via the dataset property. The Tom Select element is then initialized from the attributes in the dataset property.

TomSelectWidget

Base autocomplete widget. The arguments of TomSelectWidget are:

Argument Default value Description
model required the model class that provides the choices
url "autocomplete" URL pattern name of the autocomplete view
value_field f"{model._meta.pk.name}" model field that provides the value of an option
label_field getattr(model, "name_field", "name") model field that provides the label of an option
search_lookups [f"{self.value_field}__icontains", f"{self.label_field}__icontains"] the list of lookups to use when filtering the results
create_field "" model field to create new objects with (see below)
multiple False if True, allow selecting multiple options
listview_url "" URL name of the list view for this model (see below)
add_url "" URL name of the add view for this model(see below)
filter_by () a 2-tuple defining an additional filter (see below)
bootstrap_version 5 the bootstrap version to use, either 4 or 5

TomSelectTabularWidget

This widget displays the results in tabular form. A table header will be added to the dropdown. By default, the table contains two columns: one column for the choice value (commonly the "ID" of the option) and one column for the choice label (the human-readable part of the choice).

Tabular select preview

TomSelectTabularWidget has the following additional arguments:

Argument Default value Description
extra_columns a mapping for additional columns
value_field_label f"{value_field.title()}" table header for the value column
label_field_label f"{model._meta.verbose_name}" table header for the label column
label_field_label f"{model._meta.verbose_name}" table header for the label column
show_value_field False show the value field column (typically id)

Adding more columns

To add more columns, pass a dictionary mapping field names to column labels as extra_columns to the widget's arguments.

from django import forms
from django_tomselect.widgets import TomSelectTabularWidget
from .models import Person


class MyForm(forms.Form):
    person = forms.ModelChoiceField(
        Person.objects.all(),
        widget=TomSelectTabularWidget(
            Person,
            url="my_autocomplete_view",
            # for extra columns pass a mapping of {"model_field": "Column Header Label"}
            extra_columns={"first_name": "First Name", "last_name": "Last Name"},
        ),
    )

The column label is the header label for a given column in the table.

The attribute name tells Tom Select what value to look up on a result for the column.

Important: that means that the result visible to Tom Select must have an attribute or property with that name or the column will remain empty. The results for Tom Select are created by the view calling values() on the result queryset, so you must make sure that the attribute name is available on the view's root queryset as either a model field or as an annotation.


Settings

TOMSELECT_BOOTSTRAP_VERSION

The bootstrap version to use. Either 4 or 5. Defaults to 5.

This sets the project-wide default for the bootstrap_version argument of the widgets. You can overwrite the default for a specific widget by passing the bootstrap_version argument to the widget.


Function & Features

Modifying the initial QuerySet

If you want to modify all autocomplete queries for a subclassed AutocompleteView, you can use super() with the get_queryset() method.

from django_tomselect.views import AutocompleteView


class MyAutocompleteView(AutocompleteView):
    def get_queryset(self):
        """Toy example of filtering all queries in this view to id values less than 10"""
        queryset = super().get_queryset()
        queryset.filter(id__lt=10)
        return queryset

Searching

The AutocompleteView filters the result QuerySet against the search_lookups passed to the widget. The default value for the lookup is name__icontains. Overwrite the AutocompleteView.search method to modify the search process.

from django_tomselect.views import AutocompleteView


class MyAutocompleteView(AutocompleteView):
    def search(self, queryset, q):
        # Filter using your own queryset method:
        return queryset.search(q)

Option creation

To enable option creation in the dropdown, pass the URL pattern name of the add page of the given model to the widget. This will add an 'Add' button to the bottom of the dropdown.

# urls.py
from django.urls import path
from django_tomselect.views import AutocompleteView
from django_tomselect.widgets import TomSelectWidget
from .models import City
from .views import CityAddView

urlpatterns = [
    # ...
    path("autocomplete/", AutocompleteView.as_view(), name="my_autocomplete_view"),
    path("city/add/", CityAddView.as_view(), name="city_add"),
]

# forms.py
widget = TomSelectWidget(City, url="my_autocomplete_view", add_url="city_add")

Clicking on that button sends the user to the add page of the model.

AJAX request

If create_field was also passed to the widget, clicking on the button will create a new object using an AJAX POST request to the autocomplete URL. The autocomplete view will use the search term that the user put in on the create_field to create the object:

class AutocompleteView:
    
    def create_object(self, data):
        """Create a new object with the given data."""
        return self.model.objects.create(**{self.create_field: data[self.create_field]})

Override the view's create_object method to change the creation process.

List View link

The dropdown will include a link to the list view of the given model if you pass in the URL pattern name of the list view.

# urls.py
from django.urls import path
from django_tomselect.views import AutocompleteView
from django_tomselect.widgets import TomSelectWidget
from .models import City
from .views import CityListView

urlpatterns = [
    # ...
    path("autocomplete/", AutocompleteView.as_view(), name="my_autocomplete_view"),
    path("city/list/", CityListView.as_view(), name="city_listview"),
]

# forms.py
widget = TomSelectWidget(City, url="my_autocomplete_view", listview_url="city_listview")

Chained Dropdown Filtering

Use the filter_by argument to restrict the available options of one TomSelectWidget to the value selected in another form field. The parameter must be a 2-tuple: (name_of_the_other_form_field, django_field_lookup)

# models.py
from django import forms
from django.db import models
from django_tomselect.widgets import TomSelectWidget


class Person(models.Model):
    name = models.CharField(max_length=50)
    city = models.ForeignKey("City", on_delete=models.SET_NULL, blank=True, null=True)


class City(models.Model):
    name = models.CharField(max_length=50)
    is_capitol = models.BooleanField(default=False)


# forms.py
class PersonsFromCapitolsForm(forms.Form):
    capitol = forms.ModelChoiceField(queryset=City.objects.filter(is_capitol=True))
    person = forms.ModelChoiceField(
        queryset=Person.objects.all(),
        widget=TomSelectWidget(Person, filter_by=("capitol", "city_id")),
    )

This will result in the Person result queryset to be filtered against city_id for the currently selected capitol formfield value.
NOTE: When using filter_by, the declaring element now requires that the other field provides a value, since its choices are dependent on the other field. If the other field does not have a value, the search will not return any results.


Development & Demo

python3 -m venv venv
source venv/bin/activate
make init

Then see the demo for a preview: python demo/manage.py runserver

Run tests with make test or make tox. To install required browsers for playwright: playwright install. See the makefile for other commands.

Project details


Release history Release notifications | RSS feed

This version

0.4.2

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-tomselect-0.4.2.tar.gz (64.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_tomselect-0.4.2-py3-none-any.whl (59.6 kB view details)

Uploaded Python 3

File details

Details for the file django-tomselect-0.4.2.tar.gz.

File metadata

  • Download URL: django-tomselect-0.4.2.tar.gz
  • Upload date:
  • Size: 64.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.6

File hashes

Hashes for django-tomselect-0.4.2.tar.gz
Algorithm Hash digest
SHA256 a35afabdd46b92cd88fdb4d1f211e39ccbafa03c82c8549358a4ffa6ba859e82
MD5 3002bd8e9a7cd1c3e1a25fd74f7b386e
BLAKE2b-256 57aa03a915675cf1c93b0ab2e61b5f0bbe8c3c4994174b5e18f9bc40d373a000

See more details on using hashes here.

File details

Details for the file django_tomselect-0.4.2-py3-none-any.whl.

File metadata

File hashes

Hashes for django_tomselect-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d1deb341f885d97ffa9125f705f0323d6d747cce5487775feb1414abbf09e1dd
MD5 71f6b0b99ea41e0eba1937f99962e721
BLAKE2b-256 77dac93a0e25e85395e4276e33e05afe3d1932c535707e3a4d7710c7d05861eb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page