Working with Models¶
Using django_tomselect with Django models involves setting up dedicated autocomplete views that return JSON responses to your TomSelect widgets, customizing querysets for dynamic filtering, and configuring search behavior. This section covers the basic concepts you will need to integrate TomSelect fields into your Django models and forms.
Setting up Autocomplete Views¶
To use TomSelect widgets with Django model data, you must create a view that extends AutocompleteModelView. This view handles incoming requests from the widget, fetches matching model instances, and returns a JSON response formatted for TomSelect.
# autocompletes.py
from django_tomselect.autocompletes import AutocompleteModelView
from .models import Author
class AuthorAutocompleteView(AutocompleteModelView):
model = Author
search_lookups = ["name__icontains", "bio__icontains"]
ordering = ["name"]
page_size = 20 # Limit how many results to return per request
# urls.py
from django.urls import path
from .autocompletes import AuthorAutocompleteView
urlpatterns = [
path("autocomplete/author/", AuthorAutocompleteView.as_view(), name="author-autocomplete"),
]
By extending AutocompleteModelView, you gain access to built-in pagination, optional permission handling, and straightforward customization points like search_lookups and ordering.
Customizing Queryset Filtering¶
When working with related data or conditional dropdowns, you may need to dynamically filter the options returned by the autocomplete view. django_tomselect supports passing filtering parameters from the widget to the view through filter_by and exclude_by. These parameters allow the view to return only relevant model instances.
For example, you might have a dependent field that filters Edition instances based on a selected Magazine. In your view, you could implement the logic as follows:
from django.db.models import F, Q
from django_tomselect.autocompletes import AutocompleteModelView
from .models import Edition
class EditionAutocompleteView(AutocompleteModelView):
model = Edition
search_lookups = ["name__icontains"]
def apply_filters(self, queryset):
# The filter_by parameter will be in the format: "magazine__id=<value>"
# If the widget sends this parameter, filter editions by the given magazine ID.
return super().apply_filters(queryset)
If the frontend widget includes filter_by=("magazine", "magazine_id") in its configuration, the widget will automatically append something like ?f='magazine__id=1' to the AJAX request once a magazine is selected. The apply_filters method will then restrict the queryset accordingly.
Similarly, exclude_by works the same way but excludes matching objects. For example, if you want to prevent a primary author from appearing in a list of contributing authors, you can use exclude_by=("primary_author", "id") in the widget config. The view will receive e='primary_author__id=<value>' and remove that author from the results.
Model Field Relationships¶
django_tomselect can work seamlessly with various model relationship types, such as ForeignKey, ManyToManyField, and OneToOneField. When bound to ModelChoiceField or ModelMultipleChoiceField subclasses, the widget will handle the standard relational logic:
ForeignKey fields: Single selection of a related instance.
ManyToManyField fields: Multiple selection from a related model.
Chained relationships: Filter results based on the currently selected related object, as shown with
filter_byandexclude_by.
Your autocomplete views should return the relevant model instances, and the widget will ensure the correct options are presented to the user.
Implementing Search¶
Searching is a key feature of AutocompleteModelView. By setting search_lookups, you define which model fields should be searched against the user’s query.
class AuthorAutocompleteView(AutocompleteModelView):
model = Author
search_lookups = [
"name__icontains",
"bio__icontains"
]
# Optionally override search for more complex queries:
def search(self, queryset, query):
if not query:
return queryset
# Combine Q lookups to implement multi-field searches
q_objects = Q()
for lookup in self.search_lookups:
q_objects |= Q(**{lookup: query})
return queryset.filter(q_objects).distinct()
By default, search_lookups are used directly. If you need more advanced logic-such as searching multiple related fields, adding custom filters, or combining conditions-override the search() method.
Example View Implementation¶
Below is a sample implementation that ties everything together. This view filters results, applies search logic, orders the queryset, and handles dynamic parameters from the widget:
from django.db.models import Count, Q, F
from django_tomselect.autocompletes import AutocompleteModelView
from .models import Category
class CategoryAutocompleteView(AutocompleteModelView):
model = Category
search_lookups = ["name__icontains", "parent__name__icontains"]
ordering = ["name"]
page_size = 20
def hook_queryset(self, queryset):
# Annotate with counts or other prefetch logic
return queryset.annotate(article_count=Count("article"))
def apply_filters(self, queryset):
# Use built-in filtering logic. If `f='parent__id=<value>'` is provided,
# this method will apply that filter automatically.
return super().apply_filters(queryset)
def search(self, queryset, query):
# Add custom search conditions in addition to the configured search_lookups
if query:
q_objects = Q()
for lookup in self.search_lookups:
q_objects |= Q(**{lookup: query})
# Add custom fields to search:
q_objects |= Q(articles__title__icontains=query)
return queryset.filter(q_objects).distinct()
return queryset
def prepare_results(self, results):
# Format results for JSON response
data = []
for cat in results:
data.append({
"id": cat.id,
"name": cat.name,
"article_count": cat.article_count,
})
return data
This example demonstrates how to integrate all aspects of model-based autocompletes: searching, filtering, ordering, and result preparation. With these fundamentals in place, you can easily incorporate TomSelect widgets into your Django forms and leverage your models’ relationships to provide a responsive, intuitive selection interface for your users.